<commit_before>package codeutilsShared

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// CopyDirectory
// This function will copy a directory, sub-directories, and files
func CopyDirectory(sourceDirectory, destinationDirectory string) error {
	var copyError error

	if IsDir(sourceDirectory) { // If sourceDirectory is a valid directory
		os.MkdirAll(destinationDirectory, UniversalFileMode)                     // Make all the needed directories to destinationDirectory
		sourceDirectoryFile, _ := os.Open(sourceDirectory)                       // Get the source directory "file" struct
		directoryContents, directoryReadError := sourceDirectoryFile.Readdir(-1) // Read the directory contents

		if directoryReadError == nil { /// If there was no read error on the directory
			if len(directoryContents) != 0 { // If there is content
				for _, contentItemFileInfo := range directoryContents { // For each FileInfo struct in directoryContents
					contentItemName := contentItemFileInfo.Name() // Get the name of the item
					sourceItemPath := sourceDirectory + "/" + contentItemName
					destinationItemPath := destinationDirectory + "/" + contentItemName

					if contentItemFileInfo.IsDir() { // If this is a directory
						copyError = CopyDirectory(sourceItemPath, destinationItemPath) // Copy this sub-directory and its contents
					} else { // If this is a file
						copyError = CopyFile(sourceItemPath, destinationItemPath) // Copy the directory
					}
				}
			}
		} else { // If there was a read error on the directory
			copyError = errors.New("Unable to read: " + sourceDirectory)
		}
	} else { // If sourceDirectory is not a valid directory
		copyError = errors.New(sourceDirectory + " is not a valid directory.")
	}

	return copyError
}

// CopyFile
// This function will copy a file and its relevant permissions
func CopyFile(sourceFile, destinationFile string) error {
	var copyError error

	sourceFileStruct, sourceFileError := os.Open(sourceFile) // Attempt to open the sourceFile

	if sourceFileError == nil { // If there was not an error opening the source file
		sourceFileStats, _ := sourceFileStruct.Stat() // Get the stats of the file

		if sourceFileStats.IsDir() { // If this is actually a directory
			copyError = errors.New(sourceFile + " is a directory. Please use CopyDirectory instead.")
		} else { // If it is indeed a file
			sourceFileMode := sourceFileStats.Mode() // Get the FileMode of this file
			sourceFileStruct.Close()                 // Close the file

			fileContent, _ := ioutil.ReadFile(sourceFile) // Read the source file
			copyError = WriteOrUpdateFile(destinationFile, fileContent, sourceFileMode)
		}
	} else { // If the file does not exist
		copyError = errors.New(sourceFile + " does not exist.")
	}

	return copyError
}

// FindClosestFile
// This function will return the closest related file to the one provided from a specific path
// Returns: string (Closest file) or error (such as if path doesn't exist)
func FindClosestFile(file string) (string, error) {
	var closestFile string
	var closestFileError error

	directory := filepath.Dir(file) + "/" // Get the directory of the file
	file = filepath.Base(file)            // Change file to only being the base (file name and extension)

	if _, fileReadError := ioutil.ReadFile(directory + file); fileReadError != nil { // If there was a read error for the file
		fileNoCaps := strings.ToLower(file)                            // Set fileNoCaps to the file name with no caps
		fileNoHyphen := strings.Replace(file, "-", "", -1)             // Set fileNoHyphen to the file name with no -
		fileNoCapsNoHyphen := strings.Replace(fileNoCaps, "-", "", -1) // Set fileNoCapsNoHyphen to file name with no caps or -

		fileClosestAttempts := []string{fileNoCaps, fileNoHyphen, fileNoCapsNoHyphen} // Set fileClosestAttempts to an array of the closest attempts strings

		for _, fileAttemptName := range fileClosestAttempts {
			_, fileAttemptReadError := ioutil.ReadFile(directory + fileAttemptName) // Attempt to read this file

			if fileAttemptReadError == nil { // If we successfully found a match
				closestFile = fileAttemptName // Set closest file to this attempt name
				break
			}
		}

		if closestFile == "" { // If no matches were found
			closestFileError = errors.New("No match for " + directory + file + " found.")
		}
	} else { // If there was no read error for the file, meaning the closest file is the file itself
		closestFile = file // Set to original file provided
	}

	return directory + closestFile, closestFileError
}

// GetFiles
// This function will get all the files from a directory.
func GetFiles(directoryName string) ([]string, error) {
	var files []string      // Define files as a []string
	var getFilesError error // Define getFilesError as an error

	if IsDir(directoryName) { // If directoryName is a directory
		var directory *os.File
		directory, _ = os.Open(directoryName)
		directoryContents, directoryReadError := directory.Readdir(-1)

		if directoryReadError == nil { // If there was no issue reading the directory contents
			for _, fileInfoStruct := range directoryContents { // For each FileInfo struct in directoryContents
				if !fileInfoStruct.IsDir() { // If the FileInfo indicates the object is not a directory
					files = append(files, fileInfoStruct.Name()) // Add to files the file's name
				}
			}
		} else { // If there was ano issue reading the directory content
			getFilesError = errors.New("Cannot read the contents of " + directoryName)
		}
	} else { // If directoryName is not a directory
		getFilesError = errors.New(directoryName + " is not a directory.")
	}

	return files, getFilesError
}

// GetFilesContains
// This function will return any files from a directory containing a particular string
func GetFilesContains(directoryName, substring string) ([]string, error) {
	var files []string                // Define files as the parsed files
	var getFilesError error           // Define getFilesError as an error
	var allDirectoryContents []string // Define allDirectoryContents as the contents returned (if any) from GetFiles

	allDirectoryContents, getFilesError = GetFiles(directoryName) // Get all the files from the directoryName

	if getFilesError == nil { // If there was no issue getting the directory contents
		for _, fileName := range allDirectoryContents { // For each file name in directory contents
			if strings.Contains(fileName, substring) { // If the file name contains our substring
				files = append(files, fileName) // Append to files
			}
		}
	}

	return files, getFilesError
}

// IsDir
// Checks if the string provided is a directory or not (based on the current working directory)
func IsDir(path string) bool {
	var isDir bool
	currentWorkingDirectory, _ := os.Getwd()
	fileObject, fileOpenError := os.Open(currentWorkingDirectory + "/" + path) // Open currentDirectory + path

	if fileOpenError == nil { // If there was no error opening the file object
		fileStatistics, filePathError := fileObject.Stat() // Get any stats

		if filePathError == nil { // If we got the statistics properly
			isDir = fileStatistics.IsDir() // Set isDir to result from fileStatistics
		}
	}

	return isDir
}

// WriteOrUpdateFile
// Function to write or update the file contents of the passed file under the leading filepath with the specified sourceFileMode
func WriteOrUpdateFile(file string, fileContent []byte, sourceFileMode os.FileMode) error {
	destinationFileDirectories := filepath.Dir(file) // Get the directories leading up to the destinationFileDirectories

	if sourceFileMode == 0777 { // If things are global rwe
		sourceFileMode = UniversalFileMode // No, I can't let you do that Dave. (Changes to 744)
	}

	if destinationFileDirectories != "." { // If this is not the same directory as working dir
		os.MkdirAll(destinationFileDirectories, sourceFileMode) // Make all the necessary directories
	}

	return ioutil.WriteFile(file, fileContent, sourceFileMode) // Write the file content
}
<commit_msg>Ensure we add the directory name to the file name in GetFiles.<commit_after>package codeutilsShared

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

// CopyDirectory
// This function will copy a directory, sub-directories, and files
func CopyDirectory(sourceDirectory, destinationDirectory string) error {
	var copyError error

	if IsDir(sourceDirectory) { // If sourceDirectory is a valid directory
		os.MkdirAll(destinationDirectory, UniversalFileMode)                     // Make all the needed directories to destinationDirectory
		sourceDirectoryFile, _ := os.Open(sourceDirectory)                       // Get the source directory "file" struct
		directoryContents, directoryReadError := sourceDirectoryFile.Readdir(-1) // Read the directory contents

		if directoryReadError == nil { /// If there was no read error on the directory
			if len(directoryContents) != 0 { // If there is content
				for _, contentItemFileInfo := range directoryContents { // For each FileInfo struct in directoryContents
					contentItemName := contentItemFileInfo.Name() // Get the name of the item
					sourceItemPath := sourceDirectory + "/" + contentItemName
					destinationItemPath := destinationDirectory + "/" + contentItemName

					if contentItemFileInfo.IsDir() { // If this is a directory
						copyError = CopyDirectory(sourceItemPath, destinationItemPath) // Copy this sub-directory and its contents
					} else { // If this is a file
						copyError = CopyFile(sourceItemPath, destinationItemPath) // Copy the directory
					}
				}
			}
		} else { // If there was a read error on the directory
			copyError = errors.New("Unable to read: " + sourceDirectory)
		}
	} else { // If sourceDirectory is not a valid directory
		copyError = errors.New(sourceDirectory + " is not a valid directory.")
	}

	return copyError
}

// CopyFile
// This function will copy a file and its relevant permissions
func CopyFile(sourceFile, destinationFile string) error {
	var copyError error

	sourceFileStruct, sourceFileError := os.Open(sourceFile) // Attempt to open the sourceFile

	if sourceFileError == nil { // If there was not an error opening the source file
		sourceFileStats, _ := sourceFileStruct.Stat() // Get the stats of the file

		if sourceFileStats.IsDir() { // If this is actually a directory
			copyError = errors.New(sourceFile + " is a directory. Please use CopyDirectory instead.")
		} else { // If it is indeed a file
			sourceFileMode := sourceFileStats.Mode() // Get the FileMode of this file
			sourceFileStruct.Close()                 // Close the file

			fileContent, _ := ioutil.ReadFile(sourceFile) // Read the source file
			copyError = WriteOrUpdateFile(destinationFile, fileContent, sourceFileMode)
		}
	} else { // If the file does not exist
		copyError = errors.New(sourceFile + " does not exist.")
	}

	return copyError
}

// FindClosestFile
// This function will return the closest related file to the one provided from a specific path
// Returns: string (Closest file) or error (such as if path doesn't exist)
func FindClosestFile(file string) (string, error) {
	var closestFile string
	var closestFileError error

	directory := filepath.Dir(file) + "/" // Get the directory of the file
	file = filepath.Base(file)            // Change file to only being the base (file name and extension)

	if _, fileReadError := ioutil.ReadFile(directory + file); fileReadError != nil { // If there was a read error for the file
		fileNoCaps := strings.ToLower(file)                            // Set fileNoCaps to the file name with no caps
		fileNoHyphen := strings.Replace(file, "-", "", -1)             // Set fileNoHyphen to the file name with no -
		fileNoCapsNoHyphen := strings.Replace(fileNoCaps, "-", "", -1) // Set fileNoCapsNoHyphen to file name with no caps or -

		fileClosestAttempts := []string{fileNoCaps, fileNoHyphen, fileNoCapsNoHyphen} // Set fileClosestAttempts to an array of the closest attempts strings

		for _, fileAttemptName := range fileClosestAttempts {
			_, fileAttemptReadError := ioutil.ReadFile(directory + fileAttemptName) // Attempt to read this file

			if fileAttemptReadError == nil { // If we successfully found a match
				closestFile = fileAttemptName // Set closest file to this attempt name
				break
			}
		}

		if closestFile == "" { // If no matches were found
			closestFileError = errors.New("No match for " + directory + file + " found.")
		}
	} else { // If there was no read error for the file, meaning the closest file is the file itself
		closestFile = file // Set to original file provided
	}

	return directory + closestFile, closestFileError
}

// GetFiles
// This function will get all the files from a directory.
func GetFiles(directoryName string) ([]string, error) {
	var files []string      // Define files as a []string
	var getFilesError error // Define getFilesError as an error

	if IsDir(directoryName) { // If directoryName is a directory
		var directory *os.File
		directory, _ = os.Open(directoryName)
		directoryContents, directoryReadError := directory.Readdir(-1)

		if directoryReadError == nil { // If there was no issue reading the directory contents
			for _, fileInfoStruct := range directoryContents { // For each FileInfo struct in directoryContents
				if !fileInfoStruct.IsDir() { // If the FileInfo indicates the object is not a directory
					files = append(files, directoryName+"/"+fileInfoStruct.Name()) // Add to files the file's name
				}
			}
		} else { // If there was ano issue reading the directory content
			getFilesError = errors.New("Cannot read the contents of " + directoryName)
		}
	} else { // If directoryName is not a directory
		getFilesError = errors.New(directoryName + " is not a directory.")
	}

	return files, getFilesError
}

// GetFilesContains
// This function will return any files from a directory containing a particular string
func GetFilesContains(directoryName, substring string) ([]string, error) {
	var files []string                // Define files as the parsed files
	var getFilesError error           // Define getFilesError as an error
	var allDirectoryContents []string // Define allDirectoryContents as the contents returned (if any) from GetFiles

	allDirectoryContents, getFilesError = GetFiles(directoryName) // Get all the files from the directoryName

	if getFilesError == nil { // If there was no issue getting the directory contents
		for _, fileName := range allDirectoryContents { // For each file name in directory contents
			if strings.Contains(fileName, substring) { // If the file name contains our substring
				files = append(files, fileName) // Append to files
			}
		}
	}

	return files, getFilesError
}

// IsDir
// Checks if the string provided is a directory or not (based on the current working directory)
func IsDir(path string) bool {
	var isDir bool
	currentWorkingDirectory, _ := os.Getwd()
	fileObject, fileOpenError := os.Open(currentWorkingDirectory + "/" + path) // Open currentDirectory + path

	if fileOpenError == nil { // If there was no error opening the file object
		fileStatistics, filePathError := fileObject.Stat() // Get any stats

		if filePathError == nil { // If we got the statistics properly
			isDir = fileStatistics.IsDir() // Set isDir to result from fileStatistics
		}
	}

	return isDir
}

// WriteOrUpdateFile
// Function to write or update the file contents of the passed file under the leading filepath with the specified sourceFileMode
func WriteOrUpdateFile(file string, fileContent []byte, sourceFileMode os.FileMode) error {
	destinationFileDirectories := filepath.Dir(file) // Get the directories leading up to the destinationFileDirectories

	if sourceFileMode == 0777 { // If things are global rwe
		sourceFileMode = UniversalFileMode // No, I can't let you do that Dave. (Changes to 744)
	}

	if destinationFileDirectories != "." { // If this is not the same directory as working dir
		os.MkdirAll(destinationFileDirectories, sourceFileMode) // Make all the necessary directories
	}

	return ioutil.WriteFile(file, fileContent, sourceFileMode) // Write the file content
}
<|endoftext|>


<commit_before>package manet

import (
	"bytes"

	ma "github.com/multiformats/go-multiaddr"
)

// Loopback Addresses
var (
	// IP4Loopback is the ip4 loopback multiaddr
	IP4Loopback = ma.StringCast("/ip4/127.0.0.1")

	// IP6Loopback is the ip6 loopback multiaddr
	IP6Loopback = ma.StringCast("/ip6/::1")

	// IP6LinkLocalLoopback is the ip6 link-local loopback multiaddr
	IP6LinkLocalLoopback = ma.StringCast("/ip6/fe80::1")
)

// Unspecified Addresses (used for )
var (
	IP4Unspecified = ma.StringCast("/ip4/0.0.0.0")
	IP6Unspecified = ma.StringCast("/ip6/::")
)

// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
// This means: /{IP4, IP6}[/{TCP, UDP}]
func IsThinWaist(m ma.Multiaddr) bool {
	p := m.Protocols()

	// nothing? not even a waist.
	if len(p) == 0 {
		return false
	}

	if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
		return false
	}

	// only IP? still counts.
	if len(p) == 1 {
		return true
	}

	switch p[1].Code {
	case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
		return true
	default:
		return false
	}
}

// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
// This means either /ip4/127.0.0.1 or /ip6/::1
// TODO: differentiate IsIPLoopback and OverIPLoopback
func IsIPLoopback(m ma.Multiaddr) bool {
	b := m.Bytes()

	// /ip4/127 prefix (_entire_ /8 is loopback...)
	if bytes.HasPrefix(b, []byte{ma.P_IP4, 127}) {
		return true
	}

	// /ip6/::1
	if IP6Loopback.Equal(m) || IP6LinkLocalLoopback.Equal(m) {
		return true
	}

	return false
}

// IP6 Link Local addresses are non routable. The prefix is technically
// fe80::/10, but we test fe80::/16 for simplicity (no need to mask).
// So far, no hardware interfaces exist long enough to use those 2 bits.
// Send a PR if there is.
func IsIP6LinkLocal(m ma.Multiaddr) bool {
	return bytes.HasPrefix(m.Bytes(), []byte{ma.P_IP6, 0xfe, 0x80})
}

// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
// This means either /ip4/0.0.0.0 or /ip6/::
func IsIPUnspecified(m ma.Multiaddr) bool {
	return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
}
<commit_msg>Check for local ip6 addresses with more flexbility<commit_after>package manet

import (
	"bytes"

	ma "github.com/multiformats/go-multiaddr"
)

// Loopback Addresses
var (
	// IP4Loopback is the ip4 loopback multiaddr
	IP4Loopback = ma.StringCast("/ip4/127.0.0.1")

	// IP6Loopback is the ip6 loopback multiaddr
	IP6Loopback = ma.StringCast("/ip6/::1")

	// IP6LinkLocalLoopback is the ip6 link-local loopback multiaddr
	IP6LinkLocalLoopback = ma.StringCast("/ip6/fe80::1")
)

// Unspecified Addresses (used for )
var (
	IP4Unspecified = ma.StringCast("/ip4/0.0.0.0")
	IP6Unspecified = ma.StringCast("/ip6/::")
)

// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
// This means: /{IP4, IP6}[/{TCP, UDP}]
func IsThinWaist(m ma.Multiaddr) bool {
	p := m.Protocols()

	// nothing? not even a waist.
	if len(p) == 0 {
		return false
	}

	if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
		return false
	}

	// only IP? still counts.
	if len(p) == 1 {
		return true
	}

	switch p[1].Code {
	case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
		return true
	default:
		return false
	}
}

// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
// This means either /ip4/127.0.0.1 or /ip6/::1
// TODO: differentiate IsIPLoopback and OverIPLoopback
func IsIPLoopback(m ma.Multiaddr) bool {
	b := m.Bytes()

	// /ip4/127 prefix (_entire_ /8 is loopback...)
	if bytes.HasPrefix(b, []byte{ma.P_IP4, 127}) {
		return true
	}

	// /ip6/::1
	if !m.Decapsulate(IP6Loopback).Equal(m) || !m.Decapsulate(IP6LinkLocalLoopback).Equal(m) {
		return true
	}

	return false
}

// IP6 Link Local addresses are non routable. The prefix is technically
// fe80::/10, but we test fe80::/16 for simplicity (no need to mask).
// So far, no hardware interfaces exist long enough to use those 2 bits.
// Send a PR if there is.
func IsIP6LinkLocal(m ma.Multiaddr) bool {
	return bytes.HasPrefix(m.Bytes(), []byte{ma.P_IP6, 0xfe, 0x80})
}

// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
// This means either /ip4/0.0.0.0 or /ip6/::
func IsIPUnspecified(m ma.Multiaddr) bool {
	return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
}
<|endoftext|>


<commit_before>package dicebot

import (
	"fmt"
	"github.com/arkie/dicebot/roll"
	"github.com/arkie/dicebot/slack"
	"math/rand"
	"os"
	"strconv"
	"time"
)

func init() {
	r := os.Getenv("ROLL_COMMAND")
	if r == "" {
		r = "roll"
	}
	slack.Register(r, command)
}

func formatRoll(id string, mini bool, results []*roll.Dice) slack.D {
	var (
		color    string
		fields   []slack.D
		final    int
		text     string
		fallback string
	)
	for i, result := range results {
		if i == 0 {
			if result.Operator == "-" {
				final -= result.Total
			} else {
				final = result.Total
			}
			text = fmt.Sprint("*", final, "*")
			fallback = fmt.Sprint(final)
			if result.For != "" {
				text += fmt.Sprint(" for *", result.For, "*")
				fallback += fmt.Sprint(" for ", result.For)
			}
		} else {
			op := result.Operator
			switch result.Operator {
			case roll.Add:
				final += result.Total
			case roll.Subtract:
				final -= result.Total
			case roll.Multiply:
				final *= result.Total
				op = "×"
			case roll.Divide:
				final /= result.Total
			case roll.Max:
				if result.Total > final {
					final = result.Total
				}
			case roll.Min:
				if result.Total < final {
					final = result.Total
				}
			}
			text += fmt.Sprint(" ", op, " *", result.Total, "*")
			fallback += fmt.Sprint(" ", result.Operator, " ", result.Total)
			if result.For != "" {
				text += fmt.Sprint(" for *", result.For, "*")
				fallback += fmt.Sprint(" for ", result.For)
			}
			if i == len(results)-1 {
				text += fmt.Sprint(" = *", final, "*")
				fallback += fmt.Sprint(" = ", final)
			}
		}
		if result.Sides <= 1 {
			continue
		}
		rollText := fmt.Sprint(result.Rolls)
		if result.Fudge {
			if result.Total > 0 {
				color = "good"
			} else if result.Total == 0 {
				color = "warning"
			} else {
				color = "danger"
			}
		} else {
			single := result.Number * result.Sides / 3.0
			if result.Total > single*2 {
				color = "good"
			} else if result.Total > single+result.Number-1 {
				color = "warning"
			} else {
				color = "danger"
			}
		}
		dice := fmt.Sprint(result.Number, "d", result.Sides)
		if result.Fudge {
			dice = fmt.Sprint(result.Number, "df")
		}
		fields = append(fields, slack.D{
			"title": "Dice",
			"value": dice,
			"short": true,
		}, slack.D{
			"title": "Rolls",
			"value": rollText[1 : len(rollText)-1],
			"short": true,
		})
		if result.Minimum != 0 {
			count := 0
			for _, r := range result.Rolls {
				if r > result.Minimum {
					count++
				}
			}
			fields = append(fields, slack.D{
				"title": "Minimum",
				"value": strconv.Itoa(result.Minimum),
				"short": true,
			}, slack.D{
				"title": "Over",
				"value": strconv.Itoa(count),
				"short": true,
			})
		}
		if result.Maximum != 0 {
			count := 0
			for _, r := range result.Rolls {
				if r < result.Maximum {
					count++
				}
			}
			fields = append(fields, slack.D{
				"title": "Maximum",
				"value": strconv.Itoa(result.Maximum),
				"short": true,
			}, slack.D{
				"title": "Under",
				"value": strconv.Itoa(count),
				"short": true,
			})
		}
		if result.Keep != 0 {
			removed := fmt.Sprint(result.Removed)
			fields = append(fields, slack.D{
				"title": "Keep",
				"value": strconv.Itoa(result.Keep),
				"short": true,
			}, slack.D{
				"title": "Removed",
				"value": removed[1 : len(removed)-1],
				"short": true,
			})
		}
	}
	if mini {
		fields = []slack.D{}
	}
	return slack.D{
		"response_type": "in_channel",
		"attachments": []slack.D{
			{
				"fallback": fmt.Sprint("<@", id, "> rolled ", fallback),
				"text":     fmt.Sprint("<@", id, "> rolled ", text),
				// TODO: Color just uses the last color chosen.
				"color":     color,
				"fields":    fields,
				"mrkdwn_in": []string{"text"},
			},
		},
	}
}

func command(args slack.Args) slack.D {
	rand.Seed(time.Now().UnixNano())
	mini := len(args.Text) > 4 && args.Text[:4] == "mini"
	result := roll.Parse(args.Text)
	for _, roll := range result {
		roll.Roll()
	}
	return formatRoll(args.UserID, mini, result)
}
<commit_msg>Add total for for<commit_after>package dicebot

import (
	"fmt"
	"github.com/arkie/dicebot/roll"
	"github.com/arkie/dicebot/slack"
	"math/rand"
	"os"
	"strconv"
	"time"
)

func init() {
	r := os.Getenv("ROLL_COMMAND")
	if r == "" {
		r = "roll"
	}
	slack.Register(r, command)
}

func formatRoll(id string, mini bool, results []*roll.Dice) slack.D {
	var (
		color    string
		fields   []slack.D
		final    int
		text     string
		fallback string
	)
	for i, result := range results {
		if i == 0 {
			if result.Operator == "-" {
				final -= result.Total
			} else {
				final = result.Total
			}
			text = fmt.Sprint("*", final, "*")
			fallback = fmt.Sprint(final)
			if result.For != "" {
				if result.Sides > 1 {
					text += fmt.Sprint(" = *", result.Total, "*")
					fallback += fmt.Sprint(" = ", result.Total)
				}
				text += fmt.Sprint(" for *", result.For, "*")
				fallback += fmt.Sprint(" for ", result.For)
			}
		} else {
			op := result.Operator
			switch result.Operator {
			case roll.Add:
				final += result.Total
			case roll.Subtract:
				final -= result.Total
			case roll.Multiply:
				final *= result.Total
				op = "×"
			case roll.Divide:
				final /= result.Total
			case roll.Max:
				if result.Total > final {
					final = result.Total
				}
			case roll.Min:
				if result.Total < final {
					final = result.Total
				}
			}
			text += fmt.Sprint(" ", op, " *", result.Total, "*")
			fallback += fmt.Sprint(" ", result.Operator, " ", result.Total)
			if result.For != "" {
				text += fmt.Sprint(" for *", result.For, "*")
				fallback += fmt.Sprint(" for ", result.For)
			}
			if i == len(results)-1 {
				text += fmt.Sprint(" = *", final, "*")
				fallback += fmt.Sprint(" = ", final)
			}
		}
		if result.Sides <= 1 {
			continue
		}
		rollText := fmt.Sprint(result.Rolls)
		if result.Fudge {
			if result.Total > 0 {
				color = "good"
			} else if result.Total == 0 {
				color = "warning"
			} else {
				color = "danger"
			}
		} else {
			single := result.Number * result.Sides / 3.0
			if result.Total > single*2 {
				color = "good"
			} else if result.Total > single+result.Number-1 {
				color = "warning"
			} else {
				color = "danger"
			}
		}
		dice := fmt.Sprint(result.Number, "d", result.Sides)
		if result.Fudge {
			dice = fmt.Sprint(result.Number, "df")
		}
		fields = append(fields, slack.D{
			"title": "Dice",
			"value": dice,
			"short": true,
		}, slack.D{
			"title": "Rolls",
			"value": rollText[1 : len(rollText)-1],
			"short": true,
		})
		if result.Minimum != 0 {
			count := 0
			for _, r := range result.Rolls {
				if r > result.Minimum {
					count++
				}
			}
			fields = append(fields, slack.D{
				"title": "Minimum",
				"value": strconv.Itoa(result.Minimum),
				"short": true,
			}, slack.D{
				"title": "Over",
				"value": strconv.Itoa(count),
				"short": true,
			})
		}
		if result.Maximum != 0 {
			count := 0
			for _, r := range result.Rolls {
				if r < result.Maximum {
					count++
				}
			}
			fields = append(fields, slack.D{
				"title": "Maximum",
				"value": strconv.Itoa(result.Maximum),
				"short": true,
			}, slack.D{
				"title": "Under",
				"value": strconv.Itoa(count),
				"short": true,
			})
		}
		if result.Keep != 0 {
			removed := fmt.Sprint(result.Removed)
			fields = append(fields, slack.D{
				"title": "Keep",
				"value": strconv.Itoa(result.Keep),
				"short": true,
			}, slack.D{
				"title": "Removed",
				"value": removed[1 : len(removed)-1],
				"short": true,
			})
		}
	}
	if mini {
		fields = []slack.D{}
	}
	return slack.D{
		"response_type": "in_channel",
		"attachments": []slack.D{
			{
				"fallback": fmt.Sprint("<@", id, "> rolled ", fallback),
				"text":     fmt.Sprint("<@", id, "> rolled ", text),
				// TODO: Color just uses the last color chosen.
				"color":     color,
				"fields":    fields,
				"mrkdwn_in": []string{"text"},
			},
		},
	}
}

func command(args slack.Args) slack.D {
	rand.Seed(time.Now().UnixNano())
	mini := len(args.Text) > 4 && args.Text[:4] == "mini"
	result := roll.Parse(args.Text)
	for _, roll := range result {
		roll.Roll()
	}
	return formatRoll(args.UserID, mini, result)
}
<|endoftext|>


<commit_before>package main

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

	"github.com/litl/galaxy/registry"
	"github.com/litl/galaxy/utils"
	log "github.com/mailgun/gotools-log"
	"github.com/mailgun/vulcan"
	"github.com/mailgun/vulcan/endpoint"
	"github.com/mailgun/vulcan/loadbalance/roundrobin"
	"github.com/mailgun/vulcan/location/httploc"
	"github.com/mailgun/vulcan/request"
	"github.com/mailgun/vulcan/route"
	"github.com/mailgun/vulcan/route/hostroute"
)

var (
	serviceRegistry *registry.ServiceRegistry
	router          *hostroute.HostRouter
	env             string
	version         bool
	buildVersion    string
	listenAddr      string
	redisHost       string
)

type RequestLogger struct{}

func (r *RequestLogger) ObserveRequest(req request.Request) {
	log.Infof("%s %s", req.GetHttpRequest().Host, req)
}

func (r *RequestLogger) ObserveResponse(req request.Request, a request.Attempt) {}

var balancers map[string]*roundrobin.RoundRobin

func addBackends(registrations []registry.ServiceRegistration) map[string][]string {

	liveVhosts := make(map[string][]string)
	for _, r := range registrations {

		serviceConfig, err := serviceRegistry.GetServiceConfig(r.Name)
		if err != nil {
			log.Errorf("Error: %s", err)
			continue
		}
		vhosts := serviceConfig.Env()["VIRTUAL_HOST"]

		for _, vhost := range strings.Split(vhosts, ",") {

			if vhost == "" {
				continue
			}

			addr := fmt.Sprint(r.ExternalIP, ":", r.ExternalPort)
			url := "http://" + addr
			liveVhosts[vhost] = append(liveVhosts[vhost], url)
			balancer := balancers[vhost]

			if balancer == nil {
				// Create a round robin load balancer with some endpoints
				balancer, err = roundrobin.NewRoundRobin()
				if err != nil {
					log.Errorf("Error: %s", err)
					continue
				}

				// Create a http location with the load balancer we've just added
				loc, err := httploc.NewLocationWithOptions(r.Name, balancer,
					httploc.Options{
						TrustForwardHeader: true,
					})
				if err != nil {
					log.Errorf("Error: %s", err)
					continue
				}
				loc.GetObserverChain().Add("logger", &RequestLogger{})

				router.SetRouter(vhost, &route.ConstRouter{Location: loc})
				log.Infof("Creating balancer for %s", vhost)
				balancers[vhost] = balancer
			}

			// Already registered?
			if balancer.FindEndpointByUrl(url) != nil {
				continue
			}
			endpoint := endpoint.MustParseUrl(url)
			log.Infof("Adding %s endpoint %s", vhost, endpoint.GetUrl())
			err := balancer.AddEndpoint(endpoint)
			if err != nil {
				log.Warningf("%s", err)
			}
		}
	}
	return liveVhosts
}

func removeBackends(liveVhosts map[string][]string) {
	// Remove backends that are no longer registered

	remove := []string{}
	for k, _ := range balancers {

		if k == "" {
			continue
		}
		balancer := balancers[k]
		endpoints := balancer.GetEndpoints()
		for _, endpoint := range endpoints {
			vhosts := liveVhosts[k]
			exists := false
			for _, url := range vhosts {
				if endpoint.GetUrl().String() == url {
					exists = true
					break
				}
			}
			if !exists {
				log.Infof("Removing %s endpoint %s", k, endpoint.GetUrl())
				balancer.RemoveEndpoint(endpoint)
			}
		}

		endpoints = balancer.GetEndpoints()
		if len(endpoints) == 0 {
			remove = append(remove, k)
		}
	}

	for _, v := range remove {
		log.Infof("Removing balancer for %s", v)
		delete(balancers, v)
		router.RemoveRouter(v)
	}
}

func updateRoutes() {
	for {
		registrations, err := serviceRegistry.ListRegistrations()
		if err != nil {
			log.Errorf("%s", err)
			return
		}

		liveVhosts := addBackends(registrations)
		removeBackends(liveVhosts)
		time.Sleep(10 * time.Second)
	}
}

func adminHandler(w http.ResponseWriter, r *http.Request) {
	if len(balancers) == 0 {
		w.WriteHeader(503)
		return
	}
	for k, _ := range balancers {
		balancer := balancers[k]
		endpoints := balancer.GetEndpoints()
		fmt.Fprintf(w, "%s\n", k)
		for _, endpoint := range endpoints {
			fmt.Fprintf(w, "  %s\t%d\t%d\t%0.2f\n", endpoint.GetUrl(), endpoint.GetOriginalWeight(), endpoint.GetEffectiveWeight(), endpoint.GetMeter().GetRate())
		}
	}
}

func statusHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var err error
		host := r.Host
		if strings.Contains(host, ":") {
			host, _, err = net.SplitHostPort(r.Host)
			if err != nil {
				log.Warningf("%s", err)
				h.ServeHTTP(w, r)
				return
			}
		}

		if _, exists := balancers[host]; !exists {
			adminHandler(w, r)
			return
		}
		h.ServeHTTP(w, r)
	})
}

func main() {

	flag.StringVar(&listenAddr, "http", "127.0.0.1:8080", "http server address")
	flag.StringVar(&env, "env", utils.GetEnv("GALAXY_ENV", ""), "Environment namespace")
	flag.StringVar(&redisHost, "redis", utils.GetEnv("GALAXY_REDIS_HOST", "127.0.0.1:6379"), "redis host")
	flag.BoolVar(&version, "v", false, "display version info")

	flag.Parse()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if env == "" {
		fmt.Println("Need an env")
		flag.PrintDefaults()
		os.Exit(1)
	}

	balancers = make(map[string]*roundrobin.RoundRobin)
	serviceRegistry = registry.NewServiceRegistry(
		env,
		"",
		"",
		30,
		"",
	)

	serviceRegistry.Connect(redisHost)

	outputBuffer := &utils.OutputBuffer{}
	serviceRegistry.OutputBuffer = outputBuffer

	// init the logging package
	log.Init([]*log.LogConfig{
		&log.LogConfig{Name: "console"},
	})

	log.Infof("Starting quasar for env %s", env)
	log.Infof("Listening at %s", listenAddr)

	router = hostroute.NewHostRouter()
	go updateRoutes()

	proxy, err := vulcan.NewProxy(router)
	if err != nil {
		log.Fatalf("Error: %s", err)
	}

	// Proxy acts as http handler:
	server := &http.Server{
		Addr:           listenAddr,
		Handler:        statusHandler(proxy),
		ReadTimeout:    60 * time.Second,
		WriteTimeout:   60 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	log.Errorf("%s", server.ListenAndServe())
}
<commit_msg>Add more request logging details<commit_after>package main

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

	"github.com/litl/galaxy/registry"
	"github.com/litl/galaxy/utils"
	log "github.com/mailgun/gotools-log"
	"github.com/mailgun/vulcan"
	"github.com/mailgun/vulcan/endpoint"
	"github.com/mailgun/vulcan/loadbalance/roundrobin"
	"github.com/mailgun/vulcan/location/httploc"
	"github.com/mailgun/vulcan/request"
	"github.com/mailgun/vulcan/route"
	"github.com/mailgun/vulcan/route/hostroute"
)

var (
	serviceRegistry *registry.ServiceRegistry
	router          *hostroute.HostRouter
	env             string
	version         bool
	buildVersion    string
	listenAddr      string
	redisHost       string
)

type RequestLogger struct{}

func (r *RequestLogger) ObserveRequest(req request.Request) {}

func (r *RequestLogger) ObserveResponse(req request.Request, a request.Attempt) {
	err := ""
	if a.GetError() != nil {
		err = " err=" + a.GetError().Error()
	}
	log.Infof("id=%d method=%s clientIp=%s url=%s backend=%s status=%d duration=%s%s",
		req.GetId(),
		req.GetHttpRequest().Method,
		req.GetHttpRequest().RemoteAddr,
		req.GetHttpRequest().Host+req.GetHttpRequest().RequestURI,
		a.GetEndpoint(),
		a.GetResponse().StatusCode, a.GetDuration(), err)
}

var balancers map[string]*roundrobin.RoundRobin

func addBackends(registrations []registry.ServiceRegistration) map[string][]string {

	liveVhosts := make(map[string][]string)
	for _, r := range registrations {

		serviceConfig, err := serviceRegistry.GetServiceConfig(r.Name)
		if err != nil {
			log.Errorf("Error: %s", err)
			continue
		}
		vhosts := serviceConfig.Env()["VIRTUAL_HOST"]

		for _, vhost := range strings.Split(vhosts, ",") {

			if vhost == "" {
				continue
			}

			addr := fmt.Sprint(r.ExternalIP, ":", r.ExternalPort)
			url := "http://" + addr
			liveVhosts[vhost] = append(liveVhosts[vhost], url)
			balancer := balancers[vhost]

			if balancer == nil {
				// Create a round robin load balancer with some endpoints
				balancer, err = roundrobin.NewRoundRobin()
				if err != nil {
					log.Errorf("Error: %s", err)
					continue
				}

				// Create a http location with the load balancer we've just added
				loc, err := httploc.NewLocationWithOptions(r.Name, balancer,
					httploc.Options{
						TrustForwardHeader: true,
					})
				if err != nil {
					log.Errorf("Error: %s", err)
					continue
				}
				loc.GetObserverChain().Add("logger", &RequestLogger{})

				router.SetRouter(vhost, &route.ConstRouter{Location: loc})
				log.Infof("Creating balancer for %s", vhost)
				balancers[vhost] = balancer
			}

			// Already registered?
			if balancer.FindEndpointByUrl(url) != nil {
				continue
			}
			endpoint := endpoint.MustParseUrl(url)
			log.Infof("Adding %s endpoint %s", vhost, endpoint.GetUrl())
			err := balancer.AddEndpoint(endpoint)
			if err != nil {
				log.Warningf("%s", err)
			}
		}
	}
	return liveVhosts
}

func removeBackends(liveVhosts map[string][]string) {
	// Remove backends that are no longer registered

	remove := []string{}
	for k, _ := range balancers {

		if k == "" {
			continue
		}
		balancer := balancers[k]
		endpoints := balancer.GetEndpoints()
		for _, endpoint := range endpoints {
			vhosts := liveVhosts[k]
			exists := false
			for _, url := range vhosts {
				if endpoint.GetUrl().String() == url {
					exists = true
					break
				}
			}
			if !exists {
				log.Infof("Removing %s endpoint %s", k, endpoint.GetUrl())
				balancer.RemoveEndpoint(endpoint)
			}
		}

		endpoints = balancer.GetEndpoints()
		if len(endpoints) == 0 {
			remove = append(remove, k)
		}
	}

	for _, v := range remove {
		log.Infof("Removing balancer for %s", v)
		delete(balancers, v)
		router.RemoveRouter(v)
	}
}

func updateRoutes() {
	for {
		registrations, err := serviceRegistry.ListRegistrations()
		if err != nil {
			log.Errorf("%s", err)
			return
		}

		liveVhosts := addBackends(registrations)
		removeBackends(liveVhosts)
		time.Sleep(10 * time.Second)
	}
}

func adminHandler(w http.ResponseWriter, r *http.Request) {
	if len(balancers) == 0 {
		w.WriteHeader(503)
		return
	}
	for k, _ := range balancers {
		balancer := balancers[k]
		endpoints := balancer.GetEndpoints()
		fmt.Fprintf(w, "%s\n", k)
		for _, endpoint := range endpoints {
			fmt.Fprintf(w, "  %s\t%d\t%d\t%0.2f\n", endpoint.GetUrl(), endpoint.GetOriginalWeight(), endpoint.GetEffectiveWeight(), endpoint.GetMeter().GetRate())
		}
	}
}

func statusHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var err error
		host := r.Host
		if strings.Contains(host, ":") {
			host, _, err = net.SplitHostPort(r.Host)
			if err != nil {
				log.Warningf("%s", err)
				h.ServeHTTP(w, r)
				return
			}
		}

		if _, exists := balancers[host]; !exists {
			adminHandler(w, r)
			return
		}
		h.ServeHTTP(w, r)
	})
}

func main() {

	flag.StringVar(&listenAddr, "http", "127.0.0.1:8080", "http server address")
	flag.StringVar(&env, "env", utils.GetEnv("GALAXY_ENV", ""), "Environment namespace")
	flag.StringVar(&redisHost, "redis", utils.GetEnv("GALAXY_REDIS_HOST", "127.0.0.1:6379"), "redis host")
	flag.BoolVar(&version, "v", false, "display version info")

	flag.Parse()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if env == "" {
		fmt.Println("Need an env")
		flag.PrintDefaults()
		os.Exit(1)
	}

	balancers = make(map[string]*roundrobin.RoundRobin)
	serviceRegistry = registry.NewServiceRegistry(
		env,
		"",
		"",
		30,
		"",
	)

	serviceRegistry.Connect(redisHost)

	outputBuffer := &utils.OutputBuffer{}
	serviceRegistry.OutputBuffer = outputBuffer

	// init the logging package
	log.Init([]*log.LogConfig{
		&log.LogConfig{Name: "console"},
	})

	log.Infof("Starting quasar for env %s", env)
	log.Infof("Listening at %s", listenAddr)

	router = hostroute.NewHostRouter()
	go updateRoutes()

	proxy, err := vulcan.NewProxy(router)
	if err != nil {
		log.Fatalf("Error: %s", err)
	}

	// Proxy acts as http handler:
	server := &http.Server{
		Addr:           listenAddr,
		Handler:        statusHandler(proxy),
		ReadTimeout:    60 * time.Second,
		WriteTimeout:   60 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	log.Errorf("%s", server.ListenAndServe())
}
<|endoftext|>


<commit_before>package diffence

import (
	"bufio"
	"bytes"
	"io"
	"strings"
)

const (
	diffSep = "diff --git a"
)

// SplitDiffs splits a single diff txt into an individual DiffItem for each file changed
// func SplitDiffs(r io.Reader, d *Diff) error {
func SplitDiffs(r io.Reader, l List) error {

	// increase buffer size
	scanner := bufio.NewScanner(r)
	buf := make([]byte, 0, 64*1024)
	scanner.Buffer(buf, 1024*1024*10) // 10MB
	scanner.Split(ScanDiffs)

	// copy to temporary buffer because gets overwritten otherwise
	// https://golang.org/pkg/bufio/#Scanner.Bytes
	// "Bytes returns the most recent token generated by a call to Scan. The underlying array may point to data that will be OVERWRITTEN by a subsequent call to Scan. It does no allocation."
	buffer := bytes.NewBuffer(make([]byte, 0))
	BOF := true

	for scanner.Scan() {
		buffer.Write(scanner.Bytes())
		raw := buffer.String()
		// strip cruft from BOF (Beginning Of File) if necessary
		if BOF == true && !strings.HasPrefix(raw, diffSep) {
			BOF = false
			buffer.Reset()
			continue
		}
		l.Push(raw)
		buffer.Reset()
	}

	return scanner.Err()
}

// ScanDiffs splits on the diff of an inidividual file
func ScanDiffs(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	k, byteIndex, diffSepEndIndex := 0, 0, 0
	dataLen := len(data) - 1
	diffSepLen := len(diffSep)

	// loop until no more bytes left to read in this chunk of data
	for k < dataLen {
		// find the next newline
		if i := bytes.IndexByte(data[k:], '\n'); i >= 0 {
			// how far advanced already (k)
			// index after last \n char (+ i)
			// start at next byte (+ 1)
			byteIndex = k + i + 1
			diffSepEndIndex = byteIndex + diffSepLen
			if diffSepEndIndex < dataLen && string(data[byteIndex:diffSepEndIndex]) == diffSep {
				return byteIndex, dropCR(data[0 : k+i]), nil
			}
			k += i + 1
		} else {
			k = dataLen
		}
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}
<commit_msg>Increases max token size buffer to github limit (100MB)<commit_after>package diffence

import (
	"bufio"
	"bytes"
	"io"
	"strings"
)

const (
	diffSep = "diff --git a"
)

// SplitDiffs splits a single diff txt into an individual DiffItem for each file changed
// func SplitDiffs(r io.Reader, d *Diff) error {
func SplitDiffs(r io.Reader, l List) error {

	// increase buffer size
	scanner := bufio.NewScanner(r)
	buf := make([]byte, 0, 64*1024)    // 64kb starting token buffer size
	scanner.Buffer(buf, 1024*1024*100) // 100MB max token buffer size: https://help.github.com/articles/working-with-large-files/
	scanner.Split(ScanDiffs)

	// copy to temporary buffer because gets overwritten otherwise
	// https://golang.org/pkg/bufio/#Scanner.Bytes
	// "Bytes returns the most recent token generated by a call to Scan. The underlying array may point to data that will be OVERWRITTEN by a subsequent call to Scan. It does no allocation."
	buffer := bytes.NewBuffer(make([]byte, 0))
	BOF := true

	for scanner.Scan() {
		buffer.Write(scanner.Bytes())
		raw := buffer.String()
		// strip cruft from BOF (Beginning Of File) if necessary
		if BOF == true && !strings.HasPrefix(raw, diffSep) {
			BOF = false
			buffer.Reset()
			continue
		}
		l.Push(raw)
		buffer.Reset()
	}

	return scanner.Err()
}

// ScanDiffs splits on the diff of an inidividual file
func ScanDiffs(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	k, byteIndex, diffSepEndIndex := 0, 0, 0
	dataLen := len(data) - 1
	diffSepLen := len(diffSep)

	// loop until no more bytes left to read in this chunk of data
	for k < dataLen {
		// find the next newline
		if i := bytes.IndexByte(data[k:], '\n'); i >= 0 {
			// how far advanced already (k)
			// index after last \n char (+ i)
			// start at next byte (+ 1)
			byteIndex = k + i + 1
			diffSepEndIndex = byteIndex + diffSepLen
			if diffSepEndIndex < dataLen && string(data[byteIndex:diffSepEndIndex]) == diffSep {
				return byteIndex, dropCR(data[0 : k+i]), nil
			}
			k += i + 1
		} else {
			k = dataLen
		}
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/atotto/clipboard"
	"github.com/google/go-github/github"
)

var client *github.Client
var ex *exit.Exit
var pdbg *godbg.Pdbg

func init() {
	ex = exit.Default()
	if os.Getenv("dbg") != "" {
		pdbg = godbg.NewPdbg()
	} else {
		pdbg = godbg.NewPdbg(godbg.OptExcludes([]string{"/seec.go"}))
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage:       go run seec.go <sha1>")
		fmt.Println("       dbg=1 go run seec.go <sha1> for debug information")
		ex.Exit(0)
	}
	sha1 := os.Args[1]
	client = github.NewClient(nil)
	displayRateLimit()
	commit, _, err := client.Git.GetCommit("git", "git", sha1)
	if err != nil {
		fmt.Printf("Unable to get commit '%s': err '%v'\n", sha1, err)
		ex.Exit(1)
	}
	if len(commit.Parents) != 2 {
		fmt.Printf("Sha1 '%s' has '%d' parent(s) instead of 2\n", sha1, len(commit.Parents))
	}
	clogin := login(*commit.Author.Email, *commit.Author.Name)
	parent := commit.Parents[1]
	res := ""
	res = res + seeCommit(&parent, commit)
	res = res + fmt.Sprintf("(Merged by [%s -- `%s` --](https://github.com/%s) in [commit %s](https://github.com/git/git/commit/%s), %s)  ",
		*commit.Author.Name, clogin, clogin,
		sha1[:7], sha1, commit.Committer.Date.Format("02 Jan 2006"))
	fmt.Println(res)
	clipboard.WriteAll(res)
	fmt.Println("(Copied to the clipboard)")
	displayRateLimit()
}

func displayRateLimit() {
	rate, _, err := client.RateLimits()
	if err != nil {
		fmt.Printf("Error fetching rate limit: %#v\n\n", err)
	} else {
		const layout = "15:04pm (MST)"
		tc := rate.Core.Reset.Time
		tcs := fmt.Sprintf("%s", tc.Format(layout))
		ts := rate.Search.Reset.Time
		tss := fmt.Sprintf("%s", ts.Format(layout))
		fmt.Printf("\nAPI Rate Core Limit: %d/%d (reset at %s) - Search Limit: %d/%d (reset at %s)\n",
			rate.Core.Remaining, rate.Core.Limit, tcs,
			rate.Search.Remaining, rate.Search.Limit, tss)
	}
}

type commitsByAuthor struct {
	author   *github.CommitAuthor
	pcommits []*github.Commit
}

func (cba *commitsByAuthor) String() string {
	res := ""
	first := true
	for i, pcommit := range cba.pcommits {
		if !first {
			res = res + ", "
		}
		first = false
		if i == len(cba.pcommits)-1 && i > 0 {
			res = res + "and "
		}
		res = res + (*pcommit.SHA)[:7]
	}
	return fmt.Sprintf("%s=>%s", *cba.author.Name, res)
}

func seeCommit(parent, commit *github.Commit) string {
	var pcommit *github.Commit
	var err error
	for pcommit == nil {
		pcommit, _, err = client.Git.GetCommit("git", "git", *parent.SHA)
		if err != nil {
			fmt.Printf("Unable to get parent commit '%s': err '%v'\n", parent.SHA, err)
			ex.Exit(1)
		}
		// fmt.Printf("pcommit '%+v', len %d\n", pcommit, len(pcommit.Parents))
		if len(pcommit.Parents) == 2 {
			parent = &pcommit.Parents[1]
			pcommit = nil
		} else {
			break
		}
	}
	var commits = make(map[string]*commitsByAuthor)
	apcommit := pcommit
	if *pcommit.Author.Name == *commit.Author.Name {
		pdbg.Pdbgf("Same author '%s', so call checkParentCommits\nInitial message: '%s'", *pcommit.Author.Name, *commit.Message)
		apcommit = &pcommit.Parents[0]
	}
	commits = checkParentCommits(apcommit, *commit.Message)
	if len(commits) == 0 {
		pauthorname := *pcommit.Author.Name
		pcommitsByAuthor := &commitsByAuthor{pcommit.Author, []*github.Commit{pcommit}}
		commits[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("Put single commit '%s' for author '%s'", pcommitsByAuthor, pauthorname)
	}
	res := ""
	for _, pcommitsByAuthor := range commits {
		author := pcommitsByAuthor.author
		pcommits := pcommitsByAuthor.pcommits
		plogin := login(*author.Email, *author.Name)
		first := true
		for i, pcommit := range pcommits {
			if first {
				res = "See "
			} else {
				res = res + ", "
			}
			first = false
			if i == len(pcommits)-1 && i > 0 {
				res = res + "and "
			}
			c := fmt.Sprintf("[commit %s](https://github.com/git/git/commit/%s) [%s]",
				(*pcommit.SHA)[:7], *pcommit.SHA, pcommit.Author.Date.Format("02 Jan 2006"))
			res = res + c
		}
		res = res + fmt.Sprintf(" by [%s (`%s`)](https://github.com/%s).  \n",
			*author.Name, plogin, plogin)
		// seec 777e75b60568b613e452ebbb30a1fb27c4fd7d8a, https://github.com/git/git/commit/777e75b60568b613e452ebbb30a1fb27c4fd7d8a
		res = collect(res, *pcommit.Message, "Test-adapted-from")
		// seec 6dec263333417738528089834bd8cda72017aa31, https://github.com/git/git/commit/6dec263333417738528089834bd8cda72017aa31
		// seec 324a9f41cbf96ad994efc3b20be239116eba0dae, https://github.com/git/git/commit/324a9f41cbf96ad994efc3b20be239116eba0dae
		res = collect(res, *pcommit.Message, "Helped-by")
	}
	return res
}

// for cases like commit a6be52e239df4d4a469a5324273f43a0695fe95d
func checkParentCommits(apcommit *github.Commit, commitmsg string) map[string]*commitsByAuthor {
	res := make(map[string]*commitsByAuthor)
	pcommit, _, err := client.Git.GetCommit("git", "git", *apcommit.SHA)
	if err != nil {
		fmt.Printf("Unable to get check parent commit '%s': err '%v'\n", *apcommit.SHA, err)
		ex.Exit(1)
	}
	pdbg.Pdbgf("pcommit %s", *pcommit.SHA)
	pcommitmsgs := strings.Split(*pcommit.Message, "\n")
	title := pcommitmsgs[0]
	pdbg.Pdbgf("title '%s'", title)
	if strings.Contains(commitmsg, title) {
		pauthorname := *pcommit.Author.Name
		pdbg.Pdbgf("pauthorname='%s' for '%v'", pauthorname, pcommit.Author)
		pcommitsByAuthor := res[pauthorname]
		if pcommitsByAuthor == nil {
			pcommitsByAuthor = &commitsByAuthor{pcommit.Author, []*github.Commit{}}
		}
		pcommitsByAuthor.pcommits = append(pcommitsByAuthor.pcommits, pcommit)
		res[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("call checkParentCommits with parents '%+v', pca '%s' for '%s'",
			pcommit.Parents, pcommitsByAuthor.String(), pauthorname)
		ppcommits := checkParentCommits(&pcommit.Parents[0], commitmsg)
		for authorName, pcommitsByAuthor := range ppcommits {
			acommitsByAuthor := res[authorName]
			if acommitsByAuthor == nil {
				res[authorName] = pcommitsByAuthor
			} else {
				for _, pc := range pcommitsByAuthor.pcommits {
					acommitsByAuthor.pcommits = append(acommitsByAuthor.pcommits, pc)
				}
				res[authorName] = acommitsByAuthor
				pdbg.Pdbgf("Put commits '%s' for author '%s'", acommitsByAuthor.String(), authorName)
			}
		}
	}
	return res
}

func login(email string, name string) string {
	opts := &github.SearchOptions{Order: "desc"}
	var res *github.UsersSearchResult
	var err error
	if email != "" {
		res, _, err = client.Search.Users(email, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", email, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		name = strings.Replace(name, "-", " ", -1)
		res, _, err = client.Search.Users(name, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", name, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		var resIssues *github.IssuesSearchResult
		issueSearch := fmt.Sprintf(`"Signed-off-by: %s <%s>"`, name, email)
		resIssues, _, err = client.Search.Issues(issueSearch, opts)
		if err != nil {
			fmt.Printf("Unable to search issue '%s': err '%v'", issueSearch, err)
			ex.Exit(1)
		}
		if resIssues == nil || *resIssues.Total == 0 {
			return ""
		}
		issue := resIssues.Issues[0]
		return *issue.User.Login
	}
	if res == nil || *res.Total == 0 {
		return ""
	}
	return *res.Users[0].Login
}

func collect(res, msg, activity string) string {
	re := regexp.MustCompile(fmt.Sprintf(`%s:\s+([^<\r\n]+)\s+<([^>\r\n]+)>`, activity))
	activitymsg := activity + ": "
	first := true
	allresc := re.FindAllStringSubmatch(msg, -1)
	for i, resc := range allresc {
		dot := ""
		if len(resc) != 3 {
			continue
		}
		name := resc[1]
		email := resc[2]
		login := login(email, name)
		if !first {
			activitymsg = activitymsg + ", "
		}
		if i == len(allresc)-1 {
			dot = "."
			if i > 0 {
				activitymsg = activitymsg + "and "
			}
		}
		if login == "" {
			activitymsg = activitymsg + fmt.Sprintf("%s <%s>%s", name, email, dot)
			first = false
			continue
		}
		activitymsg = activitymsg + fmt.Sprintf("[%s (`%s`)](https://github.com/%s)%s", name, login, login, dot)
		first = false
	}
	if !first {
		res = res + activitymsg + "  \n"
	}
	return res
}
<commit_msg>seec.go: add commitsByDate<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/atotto/clipboard"
	"github.com/google/go-github/github"
)

var client *github.Client
var ex *exit.Exit
var pdbg *godbg.Pdbg

func init() {
	ex = exit.Default()
	if os.Getenv("dbg") != "" {
		pdbg = godbg.NewPdbg()
	} else {
		pdbg = godbg.NewPdbg(godbg.OptExcludes([]string{"/seec.go"}))
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage:       go run seec.go <sha1>")
		fmt.Println("       dbg=1 go run seec.go <sha1> for debug information")
		ex.Exit(0)
	}
	sha1 := os.Args[1]
	client = github.NewClient(nil)
	displayRateLimit()
	commit, _, err := client.Git.GetCommit("git", "git", sha1)
	if err != nil {
		fmt.Printf("Unable to get commit '%s': err '%v'\n", sha1, err)
		ex.Exit(1)
	}
	if len(commit.Parents) != 2 {
		fmt.Printf("Sha1 '%s' has '%d' parent(s) instead of 2\n", sha1, len(commit.Parents))
	}
	clogin := login(*commit.Author.Email, *commit.Author.Name)
	parent := commit.Parents[1]
	res := ""
	res = res + seeCommit(&parent, commit)
	res = res + fmt.Sprintf("(Merged by [%s -- `%s` --](https://github.com/%s) in [commit %s](https://github.com/git/git/commit/%s), %s)  ",
		*commit.Author.Name, clogin, clogin,
		sha1[:7], sha1, commit.Committer.Date.Format("02 Jan 2006"))
	fmt.Println(res)
	clipboard.WriteAll(res)
	fmt.Println("(Copied to the clipboard)")
	displayRateLimit()
}

func displayRateLimit() {
	rate, _, err := client.RateLimits()
	if err != nil {
		fmt.Printf("Error fetching rate limit: %#v\n\n", err)
	} else {
		const layout = "15:04pm (MST)"
		tc := rate.Core.Reset.Time
		tcs := fmt.Sprintf("%s", tc.Format(layout))
		ts := rate.Search.Reset.Time
		tss := fmt.Sprintf("%s", ts.Format(layout))
		fmt.Printf("\nAPI Rate Core Limit: %d/%d (reset at %s) - Search Limit: %d/%d (reset at %s)\n",
			rate.Core.Remaining, rate.Core.Limit, tcs,
			rate.Search.Remaining, rate.Search.Limit, tss)
	}
}

type commitsByAuthor struct {
	author   *github.CommitAuthor
}

// Because of seec 709cd912d4663af87903d3d278a3bab9d4d84153
type commitsByDate struct {
	date   *time.Time
	commits []*github.Commit
}

func (cba *commitsByAuthor) String() string {
	res := ""
	first := true
	for i, pcommit := range cba.pcommits {
		if !first {
			res = res + ", "
		}
		first = false
		if i == len(cba.pcommits)-1 && i > 0 {
			res = res + "and "
		}
		res = res + (*pcommit.SHA)[:7]
	}
	return fmt.Sprintf("%s=>%s", *cba.author.Name, res)
}

func seeCommit(parent, commit *github.Commit) string {
	var pcommit *github.Commit
	var err error
	for pcommit == nil {
		pcommit, _, err = client.Git.GetCommit("git", "git", *parent.SHA)
		if err != nil {
			fmt.Printf("Unable to get parent commit '%s': err '%v'\n", parent.SHA, err)
			ex.Exit(1)
		}
		// fmt.Printf("pcommit '%+v', len %d\n", pcommit, len(pcommit.Parents))
		if len(pcommit.Parents) == 2 {
			parent = &pcommit.Parents[1]
			pcommit = nil
		} else {
			break
		}
	}
	var commits = make(map[string]*commitsByAuthor)
	apcommit := pcommit
	if *pcommit.Author.Name == *commit.Author.Name {
		pdbg.Pdbgf("Same author '%s', so call checkParentCommits\nInitial message: '%s'", *pcommit.Author.Name, *commit.Message)
		apcommit = &pcommit.Parents[0]
	}
	commits = checkParentCommits(apcommit, *commit.Message)
	if len(commits) == 0 {
		pauthorname := *pcommit.Author.Name
		pcommitsByAuthor := &commitsByAuthor{pcommit.Author, []*github.Commit{pcommit}}
		commits[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("Put single commit '%s' for author '%s'", pcommitsByAuthor, pauthorname)
	}
	res := ""
	for _, pcommitsByAuthor := range commits {
		author := pcommitsByAuthor.author
		pcommits := pcommitsByAuthor.pcommits
		plogin := login(*author.Email, *author.Name)
		first := true
		for i, pcommit := range pcommits {
			if first {
				res = "See "
			} else {
				res = res + ", "
			}
			first = false
			if i == len(pcommits)-1 && i > 0 {
				res = res + "and "
			}
			c := fmt.Sprintf("[commit %s](https://github.com/git/git/commit/%s) [%s]",
				(*pcommit.SHA)[:7], *pcommit.SHA, pcommit.Author.Date.Format("02 Jan 2006"))
			res = res + c
		}
		res = res + fmt.Sprintf(" by [%s (`%s`)](https://github.com/%s).  \n",
			*author.Name, plogin, plogin)
		// seec 777e75b60568b613e452ebbb30a1fb27c4fd7d8a, https://github.com/git/git/commit/777e75b60568b613e452ebbb30a1fb27c4fd7d8a
		res = collect(res, *pcommit.Message, "Test-adapted-from")
		// seec 6dec263333417738528089834bd8cda72017aa31, https://github.com/git/git/commit/6dec263333417738528089834bd8cda72017aa31
		// seec 324a9f41cbf96ad994efc3b20be239116eba0dae, https://github.com/git/git/commit/324a9f41cbf96ad994efc3b20be239116eba0dae
		res = collect(res, *pcommit.Message, "Helped-by")
	}
	return res
}

// for cases like commit a6be52e239df4d4a469a5324273f43a0695fe95d
func checkParentCommits(apcommit *github.Commit, commitmsg string) map[string]*commitsByAuthor {
	res := make(map[string]*commitsByAuthor)
	pcommit, _, err := client.Git.GetCommit("git", "git", *apcommit.SHA)
	if err != nil {
		fmt.Printf("Unable to get check parent commit '%s': err '%v'\n", *apcommit.SHA, err)
		ex.Exit(1)
	}
	pdbg.Pdbgf("pcommit %s", *pcommit.SHA)
	pcommitmsgs := strings.Split(*pcommit.Message, "\n")
	title := pcommitmsgs[0]
	pdbg.Pdbgf("title '%s'", title)
	if strings.Contains(commitmsg, title) {
		pauthorname := *pcommit.Author.Name
		pdbg.Pdbgf("pauthorname='%s' for '%v'", pauthorname, pcommit.Author)
		pcommitsByAuthor := res[pauthorname]
		if pcommitsByAuthor == nil {
			pcommitsByAuthor = &commitsByAuthor{pcommit.Author, []*github.Commit{}}
		}
		pcommitsByAuthor.pcommits = append(pcommitsByAuthor.pcommits, pcommit)
		res[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("call checkParentCommits with parents '%+v', pca '%s' for '%s'",
			pcommit.Parents, pcommitsByAuthor.String(), pauthorname)
		ppcommits := checkParentCommits(&pcommit.Parents[0], commitmsg)
		for authorName, pcommitsByAuthor := range ppcommits {
			acommitsByAuthor := res[authorName]
			if acommitsByAuthor == nil {
				res[authorName] = pcommitsByAuthor
			} else {
				for _, pc := range pcommitsByAuthor.pcommits {
					acommitsByAuthor.pcommits = append(acommitsByAuthor.pcommits, pc)
				}
				res[authorName] = acommitsByAuthor
				pdbg.Pdbgf("Put commits '%s' for author '%s'", acommitsByAuthor.String(), authorName)
			}
		}
	}
	return res
}

func login(email string, name string) string {
	opts := &github.SearchOptions{Order: "desc"}
	var res *github.UsersSearchResult
	var err error
	if email != "" {
		res, _, err = client.Search.Users(email, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", email, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		name = strings.Replace(name, "-", " ", -1)
		res, _, err = client.Search.Users(name, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", name, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		var resIssues *github.IssuesSearchResult
		issueSearch := fmt.Sprintf(`"Signed-off-by: %s <%s>"`, name, email)
		resIssues, _, err = client.Search.Issues(issueSearch, opts)
		if err != nil {
			fmt.Printf("Unable to search issue '%s': err '%v'", issueSearch, err)
			ex.Exit(1)
		}
		if resIssues == nil || *resIssues.Total == 0 {
			return ""
		}
		issue := resIssues.Issues[0]
		return *issue.User.Login
	}
	if res == nil || *res.Total == 0 {
		return ""
	}
	return *res.Users[0].Login
}

func collect(res, msg, activity string) string {
	re := regexp.MustCompile(fmt.Sprintf(`%s:\s+([^<\r\n]+)\s+<([^>\r\n]+)>`, activity))
	activitymsg := activity + ": "
	first := true
	allresc := re.FindAllStringSubmatch(msg, -1)
	for i, resc := range allresc {
		dot := ""
		if len(resc) != 3 {
			continue
		}
		name := resc[1]
		email := resc[2]
		login := login(email, name)
		if !first {
			activitymsg = activitymsg + ", "
		}
		if i == len(allresc)-1 {
			dot = "."
			if i > 0 {
				activitymsg = activitymsg + "and "
			}
		}
		if login == "" {
			activitymsg = activitymsg + fmt.Sprintf("%s <%s>%s", name, email, dot)
			first = false
			continue
		}
		activitymsg = activitymsg + fmt.Sprintf("[%s (`%s`)](https://github.com/%s)%s", name, login, login, dot)
		first = false
	}
	if !first {
		res = res + activitymsg + "  \n"
	}
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gokyle/sshkey"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func ReadPrompt(prompt string) (in string, err error) {
	fmt.Printf("%s", prompt)
	rd := bufio.NewReader(os.Stdin)
	line, err := rd.ReadString('\n')
	if err != nil {
		return
	}
	in = strings.TrimSpace(line)
	return
}

func checkKeyType(keytype string) {
	switch keytype {
	case "rsa":
		return
	case "ecdsa":
		return
	default:
		fmt.Println("bad key type")
		os.Exit(1)
	}
}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func main() {
	flComment := flag.String("c", "", "key comment")
	flFile := flag.String("f", "", "input keyfile")
	flFpr := flag.Bool("l", false, "show fingerprint of keyfile")
	flType := flag.String("t", "ecdsa", "type of key to create")
	flSize := flag.Int("b", 256, "bitsize of key to create")
	flag.Parse()

	checkKeyType(*flType)
	if *flComment == "" {
		*flComment = getDefaultComment()
	}

	if *flType == "rsa" && *flSize == 256 {
		*flSize = 2048
	}

	if *flFpr {
		fingerprint(*flFile, *flType)
	} else {
		keygen(*flFile, *flType, *flComment, *flSize)
	}
}

func defaultKey(keytype string) string {
	home := os.Getenv("HOME")
	defkey := filepath.Join(home, ".ssh", "id_"+keytype)
	return defkey
}

func fingerprint(keyfile string, keytype string) {
	var err error

	if keyfile == "" {
		defkey := defaultKey(keytype)
		defkey += ".pub"
		prompt := fmt.Sprintf("Enter file in which the key is (%s): ", defkey)
		keyfile, err = ReadPrompt(prompt)
		checkErr(err)
		if keyfile == "" {
			keyfile = defkey
		}
	}
	pub, err := sshkey.LoadPublicKeyFile(keyfile, true)
	checkErr(err)

	fpr, err := sshkey.FingerprintPretty(pub, 0)
	checkErr(err)

	comment := pub.Comment
	if comment == "" {
		comment = keyfile
	}

	fmt.Printf("%d %s %s (%s)\n", pub.Size(), fpr, comment, strings.ToUpper(keytype))
	os.Exit(0)
}

func keygen(keyfile, keytype, comment string, size int) {
	var (
		err error
		key interface{}
	)

	switch keytype {
	case "rsa":
		fmt.Println("Generating public/private rsa key pair.")
		key, err = sshkey.GenerateSSHKey(sshkey.KEY_RSA, size)
		checkErr(err)
	case "ecdsa":
		fmt.Println("Generating public/private ecdsa key pair.")
		key, err = sshkey.GenerateSSHKey(sshkey.KEY_ECDSA, size)
		checkErr(err)
	}

	if keyfile == "" {
		defkey := defaultKey(keytype)
		prompt := fmt.Sprintf("Enter file in which to save the ke (%s): ", defkey)
		keyfile, err = ReadPrompt(prompt)
		checkErr(err)
		if keyfile == "" {
			keyfile = defkey
		}
	}

	var password string
	for {
		password, err = sshkey.PasswordPrompt("Enter password (empty for no passphrase): ")
		checkErr(err)
		if password != "" {
			var password2 string
			password2, err = sshkey.PasswordPrompt("Enter password again: ")
			checkErr(err)
			if password != password2 {
				fmt.Println("Passphrases do not match. Try again.")
				continue
			}
			break
		} else {
			break
		}
	}

	privout, err := sshkey.MarshalPrivate(key, password)
	checkErr(err)

	pub := sshkey.NewPublic(key, comment)
	if key == nil {
		fmt.Println("Failed to create a public key.")
		os.Exit(1)
	}

	pubout := sshkey.MarshalPublic(pub)
	if pubout == nil {
		fmt.Println("Failed to create a public key.")
		os.Exit(1)
	}

	err = ioutil.WriteFile(keyfile, privout, 0600)
	checkErr(err)
	err = ioutil.WriteFile(keyfile+".pub", pubout, 0644)
	checkErr(err)
}

func getDefaultComment() string {
	cmd := exec.Command("whoami")
	out, err := cmd.Output()
	if err != nil {
		return ""
	}
	whoami := strings.TrimSpace(string(out))

	cmd = exec.Command("hostname")
	out, err = cmd.Output()
	if err != nil {
		return ""
	}
	hostname := strings.TrimSpace(string(out))

	return fmt.Sprintf("%s@%s", whoami, hostname)
}
<commit_msg>avoid overwriting existing private keys<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gokyle/sshkey"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func ReadPrompt(prompt string) (in string, err error) {
	fmt.Printf("%s", prompt)
	rd := bufio.NewReader(os.Stdin)
	line, err := rd.ReadString('\n')
	if err != nil {
		return
	}
	in = strings.TrimSpace(line)
	return
}

func checkKeyType(keytype string) {
	switch keytype {
	case "rsa":
		return
	case "ecdsa":
		return
	default:
		fmt.Println("bad key type")
		os.Exit(1)
	}
}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}

func main() {
	flComment := flag.String("c", "", "key comment")
	flFile := flag.String("f", "", "input keyfile")
	flFpr := flag.Bool("l", false, "show fingerprint of keyfile")
	flType := flag.String("t", "ecdsa", "type of key to create")
	flSize := flag.Int("b", 256, "bitsize of key to create")
	flag.Parse()

	checkKeyType(*flType)
	if *flComment == "" {
		*flComment = getDefaultComment()
	}

	if *flType == "rsa" && *flSize == 256 {
		*flSize = 2048
	}

	if *flFpr {
		fingerprint(*flFile, *flType)
	} else {
		keygen(*flFile, *flType, *flComment, *flSize)
	}
}

func defaultKey(keytype string) string {
	home := os.Getenv("HOME")
	defkey := filepath.Join(home, ".ssh", "id_"+keytype)
	return defkey
}

func fingerprint(keyfile string, keytype string) {
	var err error

	if keyfile == "" {
		defkey := defaultKey(keytype)
		defkey += ".pub"
		prompt := fmt.Sprintf("Enter file in which the key is (%s): ", defkey)
		keyfile, err = ReadPrompt(prompt)
		checkErr(err)
		if keyfile == "" {
			keyfile = defkey
		}
	}
	pub, err := sshkey.LoadPublicKeyFile(keyfile, true)
	checkErr(err)

	fpr, err := sshkey.FingerprintPretty(pub, 0)
	checkErr(err)

	comment := pub.Comment
	if comment == "" {
		comment = keyfile
	}

	fmt.Printf("%d %s %s (%s)\n", pub.Size(), fpr, comment, strings.ToUpper(keytype))
	os.Exit(0)
}

func keygen(keyfile, keytype, comment string, size int) {
	var (
		err error
		key interface{}
	)

	switch keytype {
	case "rsa":
		fmt.Println("Generating public/private rsa key pair.")
		key, err = sshkey.GenerateSSHKey(sshkey.KEY_RSA, size)
		checkErr(err)
	case "ecdsa":
		fmt.Println("Generating public/private ecdsa key pair.")
		key, err = sshkey.GenerateSSHKey(sshkey.KEY_ECDSA, size)
		checkErr(err)
	}

	if keyfile == "" {
		defkey := defaultKey(keytype)
		prompt := fmt.Sprintf("Enter file in which to save the ke (%s): ", defkey)
		keyfile, err = ReadPrompt(prompt)
		checkErr(err)
		if keyfile == "" {
			keyfile = defkey
		}
	}

	if _, err = os.Stat(keyfile); !os.IsNotExist(err) {
		fmt.Printf("keyfile %q already exists.\n", keyfile)
		os.Exit(1)
	}

	var password string
	for {
		password, err = sshkey.PasswordPrompt("Enter password (empty for no passphrase): ")
		checkErr(err)
		if password != "" {
			var password2 string
			password2, err = sshkey.PasswordPrompt("Enter password again: ")
			checkErr(err)
			if password != password2 {
				fmt.Println("Passphrases do not match. Try again.")
				continue
			}
			break
		} else {
			break
		}
	}

	privout, err := sshkey.MarshalPrivate(key, password)
	checkErr(err)

	pub := sshkey.NewPublic(key, comment)
	if key == nil {
		fmt.Println("Failed to create a public key.")
		os.Exit(1)
	}

	pubout := sshkey.MarshalPublic(pub)
	if pubout == nil {
		fmt.Println("Failed to create a public key.")
		os.Exit(1)
	}

	err = ioutil.WriteFile(keyfile, privout, 0600)
	checkErr(err)
	err = ioutil.WriteFile(keyfile+".pub", pubout, 0644)
	checkErr(err)
}

func getDefaultComment() string {
	cmd := exec.Command("whoami")
	out, err := cmd.Output()
	if err != nil {
		return ""
	}
	whoami := strings.TrimSpace(string(out))

	cmd = exec.Command("hostname")
	out, err = cmd.Output()
	if err != nil {
		return ""
	}
	hostname := strings.TrimSpace(string(out))

	return fmt.Sprintf("%s@%s", whoami, hostname)
}
<|endoftext|>


<commit_before>// Package keyman provides convenience APIs around Go's built-in crypto APIs.
package keyman

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"os"
	"time"

	"github.com/getlantern/golog"
)

const (
	PEM_HEADER_PRIVATE_KEY = "RSA PRIVATE KEY"
	PEM_HEADER_PUBLIC_KEY  = "RSA PRIVATE KEY"
	PEM_HEADER_CERTIFICATE = "CERTIFICATE"
)

var (
	log = golog.LoggerFor("keyman")

	tenYearsFromToday = time.Now().AddDate(10, 0, 0)
)

// PrivateKey is a convenience wrapper for rsa.PrivateKey
type PrivateKey struct {
	rsaKey *rsa.PrivateKey
}

// Certificate is a convenience wrapper for x509.Certificate
type Certificate struct {
	cert     *x509.Certificate
	derBytes []byte
}

/*******************************************************************************
 * Private Key Functions
 ******************************************************************************/

// GeneratePK generates a PrivateKey with a specified size in bits.
func GeneratePK(bits int) (key *PrivateKey, err error) {
	var rsaKey *rsa.PrivateKey
	rsaKey, err = rsa.GenerateKey(rand.Reader, bits)
	if err == nil {
		key = &PrivateKey{rsaKey: rsaKey}
	}
	return
}

// LoadPKFromFile loads a PEM-encoded PrivateKey from a file
func LoadPKFromFile(filename string) (key *PrivateKey, err error) {
	privateKeyData, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, err
		}
		return nil, fmt.Errorf("Unable to read private key file from file %s: %s", filename, err)
	}
	block, _ := pem.Decode(privateKeyData)
	if block == nil {
		return nil, fmt.Errorf("Unable to decode PEM encoded private key data: %s", err)
	}
	rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Unable to decode X509 private key data: %s", err)
	}
	return &PrivateKey{rsaKey: rsaKey}, nil
}

// PEMEncoded encodes the PrivateKey in PEM
func (key *PrivateKey) PEMEncoded() (pemBytes []byte) {
	return pem.EncodeToMemory(key.pemBlock())
}

// WriteToFile writes the PEM-encoded PrivateKey to the given file
func (key *PrivateKey) WriteToFile(filename string) (err error) {
	keyOut, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	if err := pem.Encode(keyOut, key.pemBlock()); err != nil {
		return fmt.Errorf("Unable to PEM encode private key: %s", err)
	}
	if err := keyOut.Close(); err != nil {
		log.Debugf("Unable to close file: %v", err)
	}
	return
}

func (key *PrivateKey) pemBlock() *pem.Block {
	return &pem.Block{Type: PEM_HEADER_PRIVATE_KEY, Bytes: x509.MarshalPKCS1PrivateKey(key.rsaKey)}
}

/*******************************************************************************
 * Certificate Functions
 ******************************************************************************/

/*
Certificate() generates a certificate for the Public Key of the given PrivateKey
based on the given template and signed by the given issuer. If issuer is nil,
the generated certificate is self-signed.
*/
func (key *PrivateKey) Certificate(template *x509.Certificate, issuer *Certificate) (*Certificate, error) {
	return key.CertificateForKey(template, issuer, &key.rsaKey.PublicKey)
}

/*
CertificateForKey() generates a certificate for the given Public Key based on
the given template and signed by the given issuer.  If issuer is nil, the
generated certificate is self-signed.
*/
func (key *PrivateKey) CertificateForKey(template *x509.Certificate, issuer *Certificate, publicKey interface{}) (*Certificate, error) {
	var issuerCert *x509.Certificate
	if issuer == nil {
		// Note - for self-signed certificates, we include the host's external IP address
		issuerCert = template
	} else {
		issuerCert = issuer.cert
	}
	derBytes, err := x509.CreateCertificate(
		rand.Reader, // secure entropy
		template,    // the template for the new cert
		issuerCert,  // cert that's signing this cert
		publicKey,   // public key
		key.rsaKey,  // private key
	)
	if err != nil {
		return nil, err
	}
	return bytesToCert(derBytes)
}

// TLSCertificateFor generates a certificate useful for TLS use based on the
// given parameters.  These certs are usable for key encipherment and digital
// signatures.
//
//     organization: the org name for the cert.
//     name:         used as the common name for the cert.  If name is an IP
//                   address, it is also added as an IP SAN.
//     validUntil:   time at which certificate expires
//     isCA:         whether or not this cert is a CA
//     issuer:       the certificate which is issuing the new cert.  If nil, the
//                   new cert will be a self-signed CA certificate.
//
func (key *PrivateKey) TLSCertificateFor(
	organization string,
	name string,
	validUntil time.Time,
	isCA bool,
	issuer *Certificate) (cert *Certificate, err error) {

	template := &x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(int64(time.Now().UnixNano())),
		Subject: pkix.Name{
			Organization: []string{organization},
			CommonName:   name,
		},
		NotBefore: time.Now().AddDate(0, -1, 0),
		NotAfter:  validUntil,

		BasicConstraintsValid: true,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
	}

	// If name is an ip address, add it as an IP SAN
	ip := net.ParseIP(name)
	if ip != nil {
		template.IPAddresses = []net.IP{ip}
	}

	isSelfSigned := issuer == nil
	if isSelfSigned {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	}

	// If it's a CA, add certificate signing
	if isCA {
		template.KeyUsage = template.KeyUsage | x509.KeyUsageCertSign
		template.IsCA = true
	}

	cert, err = key.Certificate(template, issuer)
	return
}

// LoadCertificateFromFile loads a Certificate from a PEM-encoded file
func LoadCertificateFromFile(filename string) (*Certificate, error) {
	certificateData, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, err
		}
		return nil, fmt.Errorf("Unable to read certificate file from disk: %s", err)
	}
	return LoadCertificateFromPEMBytes(certificateData)
}

// LoadCertificateFromPEMBytes loads a Certificate from a byte array in PEM
// format
func LoadCertificateFromPEMBytes(pemBytes []byte) (*Certificate, error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, fmt.Errorf("Unable to decode PEM encoded certificate")
	}
	return bytesToCert(block.Bytes)
}

// LoadCertificateFromX509 loads a Certificate from an x509.Certificate
func LoadCertificateFromX509(cert *x509.Certificate) (*Certificate, error) {
	pemBytes := pem.EncodeToMemory(&pem.Block{
		Type:    "CERTIFICATE",
		Headers: nil,
		Bytes:   cert.Raw,
	})
	return LoadCertificateFromPEMBytes(pemBytes)
}

// X509 returns the x509 certificate underlying this Certificate
func (cert *Certificate) X509() *x509.Certificate {
	return cert.cert
}

// PEMEncoded encodes the Certificate in PEM
func (cert *Certificate) PEMEncoded() (pemBytes []byte) {
	return pem.EncodeToMemory(cert.pemBlock())
}

// WriteToFile writes the PEM-encoded Certificate to a file.
func (cert *Certificate) WriteToFile(filename string) (err error) {
	certOut, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	defer func() {
		if err := certOut.Close(); err != nil {
			log.Debugf("Unable to close file: %v", err)
		}
	}()
	return pem.Encode(certOut, cert.pemBlock())
}

func (cert *Certificate) WriteToTempFile() (name string, err error) {
	// Create a temp file containing the certificate
	tempFile, err := ioutil.TempFile("", "tempCert")
	if err != nil {
		return "", fmt.Errorf("Unable to create temp file: %s", err)
	}
	name = tempFile.Name()
	err = cert.WriteToFile(name)
	if err != nil {
		return "", fmt.Errorf("Unable to save certificate to temp file: %s", err)
	}
	return
}

// WriteToDERFile writes the DER-encoded Certificate to a file.
func (cert *Certificate) WriteToDERFile(filename string) (err error) {
	certOut, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	defer func() {
		if err := certOut.Close(); err != nil {
			log.Debugf("Unable to close file: %v", err)
		}
	}()
	_, err = certOut.Write(cert.derBytes)
	return err
}

// PoolContainingCert creates a pool containing this cert.
func (cert *Certificate) PoolContainingCert() *x509.CertPool {
	pool := x509.NewCertPool()
	pool.AddCert(cert.cert)
	return pool
}

// PoolContainingCerts constructs a CertPool containing all of the given certs
// (PEM encoded).
func PoolContainingCerts(certs ...string) (*x509.CertPool, error) {
	pool := x509.NewCertPool()
	for _, cert := range certs {
		c, err := LoadCertificateFromPEMBytes([]byte(cert))
		if err != nil {
			return nil, err
		}
		pool.AddCert(c.cert)
	}
	return pool, nil
}

func (cert *Certificate) ExpiresBefore(time time.Time) bool {
	return cert.cert.NotAfter.Before(time)
}

func bytesToCert(derBytes []byte) (*Certificate, error) {
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, err
	}
	return &Certificate{cert, derBytes}, nil
}

func (cert *Certificate) pemBlock() *pem.Block {
	return &pem.Block{Type: PEM_HEADER_CERTIFICATE, Bytes: cert.derBytes}
}

/*******************************************************************************
 * Utility Functions
 ******************************************************************************/

// StoredPKAndCert returns a PK and certificate for the given host, storing
// these at the given pkfile and certfile paths and using the stored values on
// subsequence calls.
func StoredPKAndCert(pkfile string, certfile string, organization string, name string) (*PrivateKey, *Certificate, error) {
	pk, err := LoadPKFromFile(pkfile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Debugf("Creating new PK at: %s", pkfile)
			pk, err = GeneratePK(2048)
			if err != nil {
				return nil, nil, err
			}
			err = pk.WriteToFile(pkfile)
			if err != nil {
				return nil, nil, fmt.Errorf("Unable to save private key: %s", err)
			}
		} else {
			return nil, nil, fmt.Errorf("Unable to read private key, even though it exists: %s", err)
		}
	}

	cert, err := LoadCertificateFromFile(certfile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Debugf("Creating new server cert at: %s", certfile)
			cert, err = pk.TLSCertificateFor(organization, name, tenYearsFromToday, true, nil)
			if err != nil {
				return nil, nil, err
			}
			err = cert.WriteToFile(certfile)
			if err != nil {
				return nil, nil, fmt.Errorf("Unable to save certificate: %s", err)
			}
		} else {
			return nil, nil, fmt.Errorf("Unable to read certificate, even though it exists: %s", err)
		}
	}

	return pk, cert, nil
}

// KeyPairFor creates a key pair for the given host, pkfile and certfile. If
// either pkfile or certfile is missing, default files will be created.
func KeyPairFor(host, pkfile, certfile string) (tls.Certificate, error) {
	mypkfile := pkfile
	if mypkfile == "" {
		mypkfile = "key.pem"
	}
	mycertfile := certfile
	if mycertfile == "" {
		mycertfile = "cert.pem"
	}
	ctx := certContext{
		PKFile:         mypkfile,
		ServerCertFile: mycertfile,
	}
	_, err1 := os.Stat(ctx.ServerCertFile)
	_, err2 := os.Stat(ctx.PKFile)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		fmt.Println("At least one of the Key/Cert files is not found -> Generating new key pair")
		err := ctx.initPKAndCert(host)
		if err != nil {
			return tls.Certificate{}, fmt.Errorf("Unable to init server cert: %s\n", err)
		}
	}

	cert, err := tls.LoadX509KeyPair(ctx.ServerCertFile, ctx.PKFile)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("Unable to load certificate and key from %s and %s: %s\n", ctx.ServerCertFile, ctx.PKFile, err)
	}
	return cert, err
}

// certContext encapsulates the certificates used by a Server
type certContext struct {
	PKFile         string
	ServerCertFile string
	PK             *PrivateKey
	ServerCert     *Certificate
}

// initPKAndCert initializes a PK + cert, creating them if necessary.
func (ctx *certContext) initPKAndCert(host string) (err error) {
	if ctx.PK, err = LoadPKFromFile(ctx.PKFile); err != nil {
		if os.IsNotExist(err) {
			fmt.Printf("Creating new PK at: %s\n", ctx.PKFile)
			if ctx.PK, err = GeneratePK(2048); err != nil {
				return
			}
			if err = ctx.PK.WriteToFile(ctx.PKFile); err != nil {
				return fmt.Errorf("Unable to save private key: %s\n", err)
			}
		} else {
			return fmt.Errorf("Unable to read private key, even though it exists: %s\n", err)
		}
	}

	fmt.Printf("Creating new cert for host %v at: %s\n", host, ctx.ServerCertFile)
	ctx.ServerCert, err = ctx.PK.TLSCertificateFor("Lantern", host, tenYearsFromToday, true, nil)
	if err != nil {
		return
	}
	err = ctx.ServerCert.WriteToFile(ctx.ServerCertFile)
	if err != nil {
		return
	}
	return nil
}
<commit_msg>Exposed underlying RSA key<commit_after>// Package keyman provides convenience APIs around Go's built-in crypto APIs.
package keyman

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"os"
	"time"

	"github.com/getlantern/golog"
)

const (
	PEM_HEADER_PRIVATE_KEY = "RSA PRIVATE KEY"
	PEM_HEADER_PUBLIC_KEY  = "RSA PRIVATE KEY"
	PEM_HEADER_CERTIFICATE = "CERTIFICATE"
)

var (
	log = golog.LoggerFor("keyman")

	tenYearsFromToday = time.Now().AddDate(10, 0, 0)
)

// PrivateKey is a convenience wrapper for rsa.PrivateKey
type PrivateKey struct {
	rsaKey *rsa.PrivateKey
}

// Certificate is a convenience wrapper for x509.Certificate
type Certificate struct {
	cert     *x509.Certificate
	derBytes []byte
}

/*******************************************************************************
 * Private Key Functions
 ******************************************************************************/

// GeneratePK generates a PrivateKey with a specified size in bits.
func GeneratePK(bits int) (key *PrivateKey, err error) {
	var rsaKey *rsa.PrivateKey
	rsaKey, err = rsa.GenerateKey(rand.Reader, bits)
	if err == nil {
		key = &PrivateKey{rsaKey: rsaKey}
	}
	return
}

// LoadPKFromFile loads a PEM-encoded PrivateKey from a file
func LoadPKFromFile(filename string) (key *PrivateKey, err error) {
	privateKeyData, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, err
		}
		return nil, fmt.Errorf("Unable to read private key file from file %s: %s", filename, err)
	}
	block, _ := pem.Decode(privateKeyData)
	if block == nil {
		return nil, fmt.Errorf("Unable to decode PEM encoded private key data: %s", err)
	}
	rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Unable to decode X509 private key data: %s", err)
	}
	return &PrivateKey{rsaKey: rsaKey}, nil
}

// RSA() returns the RSA key underlying this PrivateKey
func (key *PrivateKey) RSA() *rsa.PrivateKey {
	return key.rsaKey
}

// PEMEncoded encodes the PrivateKey in PEM
func (key *PrivateKey) PEMEncoded() (pemBytes []byte) {
	return pem.EncodeToMemory(key.pemBlock())
}

// WriteToFile writes the PEM-encoded PrivateKey to the given file
func (key *PrivateKey) WriteToFile(filename string) (err error) {
	keyOut, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	if err := pem.Encode(keyOut, key.pemBlock()); err != nil {
		return fmt.Errorf("Unable to PEM encode private key: %s", err)
	}
	if err := keyOut.Close(); err != nil {
		log.Debugf("Unable to close file: %v", err)
	}
	return
}

func (key *PrivateKey) pemBlock() *pem.Block {
	return &pem.Block{Type: PEM_HEADER_PRIVATE_KEY, Bytes: x509.MarshalPKCS1PrivateKey(key.rsaKey)}
}

/*******************************************************************************
 * Certificate Functions
 ******************************************************************************/

/*
Certificate() generates a certificate for the Public Key of the given PrivateKey
based on the given template and signed by the given issuer. If issuer is nil,
the generated certificate is self-signed.
*/
func (key *PrivateKey) Certificate(template *x509.Certificate, issuer *Certificate) (*Certificate, error) {
	return key.CertificateForKey(template, issuer, &key.rsaKey.PublicKey)
}

/*
CertificateForKey() generates a certificate for the given Public Key based on
the given template and signed by the given issuer.  If issuer is nil, the
generated certificate is self-signed.
*/
func (key *PrivateKey) CertificateForKey(template *x509.Certificate, issuer *Certificate, publicKey interface{}) (*Certificate, error) {
	var issuerCert *x509.Certificate
	if issuer == nil {
		// Note - for self-signed certificates, we include the host's external IP address
		issuerCert = template
	} else {
		issuerCert = issuer.cert
	}
	derBytes, err := x509.CreateCertificate(
		rand.Reader, // secure entropy
		template,    // the template for the new cert
		issuerCert,  // cert that's signing this cert
		publicKey,   // public key
		key.rsaKey,  // private key
	)
	if err != nil {
		return nil, err
	}
	return bytesToCert(derBytes)
}

// TLSCertificateFor generates a certificate useful for TLS use based on the
// given parameters.  These certs are usable for key encipherment and digital
// signatures.
//
//     organization: the org name for the cert.
//     name:         used as the common name for the cert.  If name is an IP
//                   address, it is also added as an IP SAN.
//     validUntil:   time at which certificate expires
//     isCA:         whether or not this cert is a CA
//     issuer:       the certificate which is issuing the new cert.  If nil, the
//                   new cert will be a self-signed CA certificate.
//
func (key *PrivateKey) TLSCertificateFor(
	organization string,
	name string,
	validUntil time.Time,
	isCA bool,
	issuer *Certificate) (cert *Certificate, err error) {

	template := &x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(int64(time.Now().UnixNano())),
		Subject: pkix.Name{
			Organization: []string{organization},
			CommonName:   name,
		},
		NotBefore: time.Now().AddDate(0, -1, 0),
		NotAfter:  validUntil,

		BasicConstraintsValid: true,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
	}

	// If name is an ip address, add it as an IP SAN
	ip := net.ParseIP(name)
	if ip != nil {
		template.IPAddresses = []net.IP{ip}
	}

	isSelfSigned := issuer == nil
	if isSelfSigned {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	}

	// If it's a CA, add certificate signing
	if isCA {
		template.KeyUsage = template.KeyUsage | x509.KeyUsageCertSign
		template.IsCA = true
	}

	cert, err = key.Certificate(template, issuer)
	return
}

// LoadCertificateFromFile loads a Certificate from a PEM-encoded file
func LoadCertificateFromFile(filename string) (*Certificate, error) {
	certificateData, err := ioutil.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, err
		}
		return nil, fmt.Errorf("Unable to read certificate file from disk: %s", err)
	}
	return LoadCertificateFromPEMBytes(certificateData)
}

// LoadCertificateFromPEMBytes loads a Certificate from a byte array in PEM
// format
func LoadCertificateFromPEMBytes(pemBytes []byte) (*Certificate, error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, fmt.Errorf("Unable to decode PEM encoded certificate")
	}
	return bytesToCert(block.Bytes)
}

// LoadCertificateFromX509 loads a Certificate from an x509.Certificate
func LoadCertificateFromX509(cert *x509.Certificate) (*Certificate, error) {
	pemBytes := pem.EncodeToMemory(&pem.Block{
		Type:    "CERTIFICATE",
		Headers: nil,
		Bytes:   cert.Raw,
	})
	return LoadCertificateFromPEMBytes(pemBytes)
}

// X509 returns the x509 certificate underlying this Certificate
func (cert *Certificate) X509() *x509.Certificate {
	return cert.cert
}

// PEMEncoded encodes the Certificate in PEM
func (cert *Certificate) PEMEncoded() (pemBytes []byte) {
	return pem.EncodeToMemory(cert.pemBlock())
}

// WriteToFile writes the PEM-encoded Certificate to a file.
func (cert *Certificate) WriteToFile(filename string) (err error) {
	certOut, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	defer func() {
		if err := certOut.Close(); err != nil {
			log.Debugf("Unable to close file: %v", err)
		}
	}()
	return pem.Encode(certOut, cert.pemBlock())
}

func (cert *Certificate) WriteToTempFile() (name string, err error) {
	// Create a temp file containing the certificate
	tempFile, err := ioutil.TempFile("", "tempCert")
	if err != nil {
		return "", fmt.Errorf("Unable to create temp file: %s", err)
	}
	name = tempFile.Name()
	err = cert.WriteToFile(name)
	if err != nil {
		return "", fmt.Errorf("Unable to save certificate to temp file: %s", err)
	}
	return
}

// WriteToDERFile writes the DER-encoded Certificate to a file.
func (cert *Certificate) WriteToDERFile(filename string) (err error) {
	certOut, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("Failed to open %s for writing: %s", filename, err)
	}
	defer func() {
		if err := certOut.Close(); err != nil {
			log.Debugf("Unable to close file: %v", err)
		}
	}()
	_, err = certOut.Write(cert.derBytes)
	return err
}

// PoolContainingCert creates a pool containing this cert.
func (cert *Certificate) PoolContainingCert() *x509.CertPool {
	pool := x509.NewCertPool()
	pool.AddCert(cert.cert)
	return pool
}

// PoolContainingCerts constructs a CertPool containing all of the given certs
// (PEM encoded).
func PoolContainingCerts(certs ...string) (*x509.CertPool, error) {
	pool := x509.NewCertPool()
	for _, cert := range certs {
		c, err := LoadCertificateFromPEMBytes([]byte(cert))
		if err != nil {
			return nil, err
		}
		pool.AddCert(c.cert)
	}
	return pool, nil
}

func (cert *Certificate) ExpiresBefore(time time.Time) bool {
	return cert.cert.NotAfter.Before(time)
}

func bytesToCert(derBytes []byte) (*Certificate, error) {
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, err
	}
	return &Certificate{cert, derBytes}, nil
}

func (cert *Certificate) pemBlock() *pem.Block {
	return &pem.Block{Type: PEM_HEADER_CERTIFICATE, Bytes: cert.derBytes}
}

/*******************************************************************************
 * Utility Functions
 ******************************************************************************/

// StoredPKAndCert returns a PK and certificate for the given host, storing
// these at the given pkfile and certfile paths and using the stored values on
// subsequence calls.
func StoredPKAndCert(pkfile string, certfile string, organization string, name string) (*PrivateKey, *Certificate, error) {
	pk, err := LoadPKFromFile(pkfile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Debugf("Creating new PK at: %s", pkfile)
			pk, err = GeneratePK(2048)
			if err != nil {
				return nil, nil, err
			}
			err = pk.WriteToFile(pkfile)
			if err != nil {
				return nil, nil, fmt.Errorf("Unable to save private key: %s", err)
			}
		} else {
			return nil, nil, fmt.Errorf("Unable to read private key, even though it exists: %s", err)
		}
	}

	cert, err := LoadCertificateFromFile(certfile)
	if err != nil {
		if os.IsNotExist(err) {
			log.Debugf("Creating new server cert at: %s", certfile)
			cert, err = pk.TLSCertificateFor(organization, name, tenYearsFromToday, true, nil)
			if err != nil {
				return nil, nil, err
			}
			err = cert.WriteToFile(certfile)
			if err != nil {
				return nil, nil, fmt.Errorf("Unable to save certificate: %s", err)
			}
		} else {
			return nil, nil, fmt.Errorf("Unable to read certificate, even though it exists: %s", err)
		}
	}

	return pk, cert, nil
}

// KeyPairFor creates a key pair for the given host, pkfile and certfile. If
// either pkfile or certfile is missing, default files will be created.
func KeyPairFor(host, pkfile, certfile string) (tls.Certificate, error) {
	mypkfile := pkfile
	if mypkfile == "" {
		mypkfile = "key.pem"
	}
	mycertfile := certfile
	if mycertfile == "" {
		mycertfile = "cert.pem"
	}
	ctx := certContext{
		PKFile:         mypkfile,
		ServerCertFile: mycertfile,
	}
	_, err1 := os.Stat(ctx.ServerCertFile)
	_, err2 := os.Stat(ctx.PKFile)
	if os.IsNotExist(err1) || os.IsNotExist(err2) {
		fmt.Println("At least one of the Key/Cert files is not found -> Generating new key pair")
		err := ctx.initPKAndCert(host)
		if err != nil {
			return tls.Certificate{}, fmt.Errorf("Unable to init server cert: %s\n", err)
		}
	}

	cert, err := tls.LoadX509KeyPair(ctx.ServerCertFile, ctx.PKFile)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("Unable to load certificate and key from %s and %s: %s\n", ctx.ServerCertFile, ctx.PKFile, err)
	}
	return cert, err
}

// certContext encapsulates the certificates used by a Server
type certContext struct {
	PKFile         string
	ServerCertFile string
	PK             *PrivateKey
	ServerCert     *Certificate
}

// initPKAndCert initializes a PK + cert, creating them if necessary.
func (ctx *certContext) initPKAndCert(host string) (err error) {
	if ctx.PK, err = LoadPKFromFile(ctx.PKFile); err != nil {
		if os.IsNotExist(err) {
			fmt.Printf("Creating new PK at: %s\n", ctx.PKFile)
			if ctx.PK, err = GeneratePK(2048); err != nil {
				return
			}
			if err = ctx.PK.WriteToFile(ctx.PKFile); err != nil {
				return fmt.Errorf("Unable to save private key: %s\n", err)
			}
		} else {
			return fmt.Errorf("Unable to read private key, even though it exists: %s\n", err)
		}
	}

	fmt.Printf("Creating new cert for host %v at: %s\n", host, ctx.ServerCertFile)
	ctx.ServerCert, err = ctx.PK.TLSCertificateFor("Lantern", host, tenYearsFromToday, true, nil)
	if err != nil {
		return
	}
	err = ctx.ServerCert.WriteToFile(ctx.ServerCertFile)
	if err != nil {
		return
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package slug provides utility functions for normalizing strings into
// "slugs". Slugs are lower-case, sanitized strings that are safe for use in
// sensitive environments like URLs.
package slug

import (
	"code.google.com/p/go.text/unicode/norm"
	"unicode"
)

var (
	// Replace non-alphanumeric characters with this byte.
	Replacement = '_'

	// The "safe" set of characters.
	alphanum = &unicode.RangeTable{
		R16: []unicode.Range16{
			{0x0030, 0x0039, 1}, // 0-9
			{0x0041, 0x005A, 1}, // A-Z
			{0x0061, 0x007A, 1}, // a-z
		},
	}
	// Characters in these ranges will be ignored.
	nop = []*unicode.RangeTable{
		unicode.Mark,
		unicode.Sk, // Symbol - modifier
		unicode.Lm, // Letter - modifier
		unicode.Cc, // Other - control
		unicode.Cf, // Other - format
	}
)

// Slug replaces each run of characters which are not ASCII letters or numbers
// with the Replacement character, except for leading or trailing runs. Letters
// will be stripped of diacritical marks and lowercased. Letter or number
// codepoints that do not have combining marks or a lower-cased variant will be
// passed through unaltered.
func Clean(s string) string {
	buf := make([]rune, 0, len(s))
	replacement := false

	for _, r := range norm.NFKD.String(s) {
		switch {
		case unicode.In(r, alphanum):
			buf = append(buf, unicode.ToLower(r))
			replacement = true
		case unicode.IsOneOf(nop, r):
			// skip
		case replacement:
			buf = append(buf, Replacement)
			replacement = false
		}
	}

	// Strip trailing Replacement byte
	if i := len(buf) - 1; i >= 0 && buf[i] == Replacement {
		buf = buf[:i]
	}

	return string(buf)
}
<commit_msg>Update text/unicode/norm package URL.<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package slug provides utility functions for normalizing strings into
// "slugs". Slugs are lower-case, sanitized strings that are safe for use in
// sensitive environments like URLs.
package slug

import (
	"unicode"

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

var (
	// Replace non-alphanumeric characters with this byte.
	Replacement = '_'

	// The "safe" set of characters.
	alphanum = &unicode.RangeTable{
		R16: []unicode.Range16{
			{0x0030, 0x0039, 1}, // 0-9
			{0x0041, 0x005A, 1}, // A-Z
			{0x0061, 0x007A, 1}, // a-z
		},
	}
	// Characters in these ranges will be ignored.
	nop = []*unicode.RangeTable{
		unicode.Mark,
		unicode.Sk, // Symbol - modifier
		unicode.Lm, // Letter - modifier
		unicode.Cc, // Other - control
		unicode.Cf, // Other - format
	}
)

// Slug replaces each run of characters which are not ASCII letters or numbers
// with the Replacement character, except for leading or trailing runs. Letters
// will be stripped of diacritical marks and lowercased. Letter or number
// codepoints that do not have combining marks or a lower-cased variant will be
// passed through unaltered.
func Clean(s string) string {
	buf := make([]rune, 0, len(s))
	replacement := false

	for _, r := range norm.NFKD.String(s) {
		switch {
		case unicode.In(r, alphanum):
			buf = append(buf, unicode.ToLower(r))
			replacement = true
		case unicode.IsOneOf(nop, r):
			// skip
		case replacement:
			buf = append(buf, Replacement)
			replacement = false
		}
	}

	// Strip trailing Replacement byte
	if i := len(buf) - 1; i >= 0 && buf[i] == Replacement {
		buf = buf[:i]
	}

	return string(buf)
}
<|endoftext|>