<commit_before>// +build ignore

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

var output = flag.String("output", "templates.go", "Where to put the variable declarations")
var pkgname = flag.String("package", "main", "What package should the output file belong to")

// errWriter wraps an io.Writer and saves the first error it encounters (and
// otherwise ignores all write-errors).
type errWriter struct {
	io.Writer
	err error
}

func main() {
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal("Must provide at least one template file to include")
	}

	buf := new(bytes.Buffer)

	fmt.Fprintf(buf, "package %s\n\n", *pkgname)
	fmt.Fprintf(buf, "// Generated by \"go run gentmpl.go %s\".\n", strings.Join(os.Args[1:], " "))
	fmt.Fprintf(buf, "// Do not edit manually.\n\n")
	fmt.Fprintf(buf, "import (\n")
	fmt.Fprintf(buf, "\t\"html/template\"\n")
	fmt.Fprintf(buf, ")\n")

	for i := 0; i < flag.NArg(); i++ {
		n := flag.Arg(i)
		b, err := ioutil.ReadFile(n + ".html")
		if err != nil {
			log.Fatal(err)
		}

		fmt.Fprintf(buf, "var %sTpl = template.Must(template.New(\"%s\").Parse(`%s`))\n\n", n, n, string(b))
	}

	outsrc, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("Could not format output: %v", err)
	}

	if err = ioutil.WriteFile(*output, outsrc, 0644); err != nil {
		log.Fatalf("Could not write output: %v", err)
	}
}
<commit_msg>gentmpl: misc improvements, set up templates for nesting<commit_after>// +build ignore

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"strings"
	"text/template"
	"unicode/utf8"
)

var output = flag.String("output", "templates.go", "Where to put the variable declarations")
var pkgname = flag.String("package", "main", "What package should the output file belong to")

// errWriter wraps an io.Writer and saves the first error it encounters (and
// otherwise ignores all write-errors).
type errWriter struct {
	io.Writer
	err error
}

// Like strconv.CanBackquote, but allows multi-line backquoted strings (i.e.
// does not consider \n in the input a cause to return false).
func canBackquote(s string) bool {
	for len(s) > 0 {
		r, wid := utf8.DecodeRuneInString(s)
		s = s[wid:]
		if wid > 1 {
			if r == '\ufeff' {
				return false // BOMs are invisible and should not be quoted.
			}
			continue // All other multibyte runes are correctly encoded and assumed printable.
		}
		if r == utf8.RuneError {
			return false
		}
		if (r < ' ' && r != '\t' && r != '\n') || r == '`' || r == '\u007F' {
			return false
		}
	}
	return true
}

var embeddedTemplateTmpl = template.Must(template.New("embedded").Parse(`
package {{ .Package }}

// Generated by "go run gentmpl.go {{ .Args }}".
// Do not edit manually.

import (
	"html/template"
)

var Templates = template.New("all")

func init() {
	{{ range $i, $t := .Templates -}}
	template.Must(Templates.New("{{ $t.Name }}").Parse({{ $t.ContentLiteral }}))
	{{ end -}}
}
`))

func main() {
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal("Must provide at least one template file to include")
	}

	type nameAndContent struct {
		Name           string
		ContentLiteral string
	}
	tmpls := make([]nameAndContent, flag.NArg())
	for i := 0; i < flag.NArg(); i++ {
		n := flag.Arg(i)
		b, err := ioutil.ReadFile(n + ".html")
		if err != nil {
			log.Fatal(err)
		}
		// If we can use backquotes, we do — it makes generated files easier to read.
		var literal string
		if canBackquote(string(b)) {
			literal = "`" + string(b) + "`"
		} else {
			literal = fmt.Sprintf("%#v", string(b))
		}
		tmpls[i] = nameAndContent{
			Name:           flag.Arg(i),
			ContentLiteral: literal,
		}
	}

	var buf bytes.Buffer
	if err := embeddedTemplateTmpl.Execute(&buf, struct {
		Package   string
		Args      string
		Templates []nameAndContent
	}{
		Package:   *pkgname,
		Args:      strings.Join(flag.Args(), " "),
		Templates: tmpls,
	}); err != nil {
		log.Fatal(err)
	}

	outsrc, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("Could not format output: %v\nOutput:\n%s", buf.String(), err)
	}

	if err := ioutil.WriteFile(*output, outsrc, 0644); err != nil {
		log.Fatalf("Could not write output: %v", err)
	}
}
<|endoftext|>


<commit_before>// +build !windows

package gexpect

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"time"
	"unicode/utf8"

	shell "github.com/kballard/go-shellquote"
	"github.com/kr/pty"
)

var (
	ErrEmptySearch = errors.New("empty search string")
	ErrTimeout     = errors.New("expect timed out")
)

type ExpectSubprocess struct {
	Cmd          *exec.Cmd
	buf          *buffer
	outputBuffer []byte
}

type buffer struct {
	f       *os.File
	b       bytes.Buffer
	collect bool

	collection bytes.Buffer
}

func (buf *buffer) StartCollecting() {
	buf.collect = true
}

func (buf *buffer) StopCollecting() (result string) {
	result = string(buf.collection.Bytes())
	buf.collect = false
	buf.collection.Reset()
	return result
}

func (buf *buffer) Read(chunk []byte) (int, error) {
	nread := 0
	if buf.b.Len() > 0 {
		n, err := buf.b.Read(chunk)
		if err != nil {
			return n, err
		}
		if n == len(chunk) {
			return n, nil
		}
		nread = n
	}
	fn, err := buf.f.Read(chunk[nread:])
	return fn + nread, err
}

func (buf *buffer) ReadRune() (r rune, size int, err error) {
	l := buf.b.Len()

	chunk := make([]byte, utf8.UTFMax)
	if l > 0 {
		n, err := buf.b.Read(chunk)
		if err != nil {
			return 0, 0, err
		}
		if utf8.FullRune(chunk) {
			r, rL := utf8.DecodeRune(chunk)
			if n > rL {
				buf.PutBack(chunk[rL:n])
			}
			if buf.collect {
				buf.collection.WriteRune(r)
			}
			return r, rL, nil
		}
	}
	// else add bytes from the file, then try that
	for l < utf8.UTFMax {
		fn, err := buf.f.Read(chunk[l : l+1])
		if err != nil {
			return 0, 0, err
		}
		l = l + fn

		if utf8.FullRune(chunk) {
			r, rL := utf8.DecodeRune(chunk)
			if buf.collect {
				buf.collection.WriteRune(r)
			}
			return r, rL, nil
		}
	}
	return 0, 0, errors.New("File is not a valid UTF=8 encoding")
}

func (buf *buffer) PutBack(chunk []byte) {
	if len(chunk) == 0 {
		return
	}
	if buf.b.Len() == 0 {
		buf.b.Write(chunk)
		return
	}
	d := make([]byte, 0, len(chunk)+buf.b.Len())
	d = append(d, chunk...)
	d = append(d, buf.b.Bytes()...)
	buf.b.Reset()
	buf.b.Write(d)
}

func SpawnAtDirectory(command string, directory string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	expect.Cmd.Dir = directory
	return _start(expect)
}

func Command(command string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	return expect, nil
}

func (expect *ExpectSubprocess) Start() error {
	_, err := _start(expect)
	return err
}

func Spawn(command string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	return _start(expect)
}

func (expect *ExpectSubprocess) Close() error {
	if err := expect.Cmd.Process.Kill(); err != nil {
		return err
	}
	if err := expect.buf.f.Close(); err != nil {
		return err
	}
	return nil
}

func (expect *ExpectSubprocess) AsyncInteractChannels() (send chan string, receive chan string) {
	receive = make(chan string)
	send = make(chan string)

	go func() {
		for {
			str, err := expect.ReadLine()
			if err != nil {
				close(receive)
				return
			}
			receive <- str
		}
	}()

	go func() {
		for {
			select {
			case sendCommand, exists := <-send:
				{
					if !exists {
						return
					}
					err := expect.Send(sendCommand)
					if err != nil {
						receive <- "gexpect Error: " + err.Error()
						return
					}
				}
			}
		}
	}()

	return
}

func (expect *ExpectSubprocess) ExpectRegex(regex string) (bool, error) {
	return regexp.MatchReader(regex, expect.buf)
}

func (expect *ExpectSubprocess) expectRegexFind(regex string, output bool) ([]string, string, error) {
	re, err := regexp.Compile(regex)
	if err != nil {
		return nil, "", err
	}
	expect.buf.StartCollecting()
	pairs := re.FindReaderSubmatchIndex(expect.buf)
	stringIndexedInto := expect.buf.StopCollecting()
	l := len(pairs)
	numPairs := l / 2
	result := make([]string, numPairs)
	for i := 0; i < numPairs; i += 1 {
		result[i] = stringIndexedInto[pairs[i*2]:pairs[i*2+1]]
	}
	// convert indexes to strings

	if len(result) == 0 {
		err = fmt.Errorf("ExpectRegex didn't find regex '%v'.", regex)
	} else {
		// The number in pairs[1] is an index of a first
		// character outside the whole match
		putBackIdx := pairs[1]
		if len(stringIndexedInto) > putBackIdx {
			stringToPutBack := stringIndexedInto[putBackIdx:]
			stringIndexedInto = stringIndexedInto[:putBackIdx]
			expect.buf.PutBack([]byte(stringToPutBack))
		}
	}
	return result, stringIndexedInto, err
}

func (expect *ExpectSubprocess) expectTimeoutRegexFind(regex string, timeout time.Duration) (result []string, out string, err error) {
	t := make(chan bool)
	go func() {
		result, out, err = expect.ExpectRegexFindWithOutput(regex)
		t <- false
	}()
	go func() {
		time.Sleep(timeout)
		err = fmt.Errorf("ExpectRegex timed out after %v finding '%v'.\nOutput:\n%s", timeout, regex, expect.Collect())
		t <- true
	}()
	<-t
	return result, out, err
}

func (expect *ExpectSubprocess) ExpectRegexFind(regex string) ([]string, error) {
	result, _, err := expect.expectRegexFind(regex, false)
	return result, err
}

func (expect *ExpectSubprocess) ExpectTimeoutRegexFind(regex string, timeout time.Duration) ([]string, error) {
	result, _, err := expect.expectTimeoutRegexFind(regex, timeout)
	return result, err
}

func (expect *ExpectSubprocess) ExpectRegexFindWithOutput(regex string) ([]string, string, error) {
	return expect.expectRegexFind(regex, true)
}

func (expect *ExpectSubprocess) ExpectTimeoutRegexFindWithOutput(regex string, timeout time.Duration) ([]string, string, error) {
	return expect.expectTimeoutRegexFind(regex, timeout)
}

func buildKMPTable(searchString string) []int {
	pos := 2
	cnd := 0
	length := len(searchString)

	var table []int
	if length < 2 {
		length = 2
	}

	table = make([]int, length)
	table[0] = -1
	table[1] = 0

	for pos < len(searchString) {
		if searchString[pos-1] == searchString[cnd] {
			cnd += 1
			table[pos] = cnd
			pos += 1
		} else if cnd > 0 {
			cnd = table[cnd]
		} else {
			table[pos] = 0
			pos += 1
		}
	}
	return table
}

func (expect *ExpectSubprocess) ExpectTimeout(searchString string, timeout time.Duration) (e error) {
	result := make(chan error)
	go func() {
		result <- expect.Expect(searchString)
	}()
	select {
	case e = <-result:
	case <-time.After(timeout):
		e = fmt.Errorf("Expect timed out after %v waiting for '%v'.\nOutput:\n%s", timeout, searchString, expect.Collect())
	}
	return e
}

func (expect *ExpectSubprocess) Expect(searchString string) (e error) {
	target := len(searchString)
	if target < 1 {
		return ErrEmptySearch
	}
	chunk := make([]byte, target*2)
	if expect.outputBuffer != nil {
		expect.outputBuffer = expect.outputBuffer[0:]
	}
	m := 0
	i := 0
	// Build KMP Table
	table := buildKMPTable(searchString)

	for {
		n, err := expect.buf.Read(chunk)
		if err != nil {
			return err
		}
		if expect.outputBuffer != nil {
			expect.outputBuffer = append(expect.outputBuffer, chunk[:n]...)
		}
		offset := m + i
		for m+i-offset < n {
			if searchString[i] == chunk[m+i-offset] {
				i += 1
				if i == target {
					unreadIndex := m + i - offset
					if len(chunk) > unreadIndex {
						expect.buf.PutBack(chunk[unreadIndex:])
					}
					return nil
				}
			} else {
				m += i - table[i]
				if table[i] > -1 {
					i = table[i]
				} else {
					i = 0
				}
			}
		}
	}
}

func (expect *ExpectSubprocess) Send(command string) error {
	_, err := io.WriteString(expect.buf.f, command)
	return err
}

func (expect *ExpectSubprocess) Capture() {
	if expect.outputBuffer == nil {
		expect.outputBuffer = make([]byte, 0)
	}
}

func (expect *ExpectSubprocess) Collect() []byte {
	collectOutput := make([]byte, len(expect.outputBuffer))
	copy(collectOutput, expect.outputBuffer)
	expect.outputBuffer = nil
	return collectOutput
}

func (expect *ExpectSubprocess) SendLine(command string) error {
	_, err := io.WriteString(expect.buf.f, command+"\r\n")
	return err
}

func (expect *ExpectSubprocess) Interact() {
	defer expect.Cmd.Wait()
	io.Copy(os.Stdout, &expect.buf.b)
	go io.Copy(os.Stdout, expect.buf.f)
	go io.Copy(expect.buf.f, os.Stdin)
}

func (expect *ExpectSubprocess) ReadUntil(delim byte) ([]byte, error) {
	join := make([]byte, 0, 512)
	chunk := make([]byte, 255)

	for {
		n, err := expect.buf.Read(chunk)

		for i := 0; i < n; i++ {
			if chunk[i] == delim {
				if len(chunk) > i+1 {
					expect.buf.PutBack(chunk[i+1:n])
				}
				return join, nil
			} else {
				join = append(join, chunk[i])
			}
		}

		if err != nil {
			return join, err
		}
	}
}

func (expect *ExpectSubprocess) Wait() error {
	return expect.Cmd.Wait()
}

func (expect *ExpectSubprocess) ReadLine() (string, error) {
	str, err := expect.ReadUntil('\n')
	return string(str), err
}

func _start(expect *ExpectSubprocess) (*ExpectSubprocess, error) {
	f, err := pty.Start(expect.Cmd)
	if err != nil {
		return nil, err
	}
	expect.buf.f = f

	return expect, nil
}

func _spawn(command string) (*ExpectSubprocess, error) {
	wrapper := new(ExpectSubprocess)

	wrapper.outputBuffer = nil

	splitArgs, err := shell.Split(command)
	if err != nil {
		return nil, err
	}
	numArguments := len(splitArgs) - 1
	if numArguments < 0 {
		return nil, errors.New("gexpect: No command given to spawn")
	}
	path, err := exec.LookPath(splitArgs[0])
	if err != nil {
		return nil, err
	}

	if numArguments >= 1 {
		wrapper.Cmd = exec.Command(path, splitArgs[1:]...)
	} else {
		wrapper.Cmd = exec.Command(path)
	}
	wrapper.buf = new(buffer)

	return wrapper, nil
}
<commit_msg>Fix buffer corruption in Expect<commit_after>// +build !windows

package gexpect

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"time"
	"unicode/utf8"

	shell "github.com/kballard/go-shellquote"
	"github.com/kr/pty"
)

var (
	ErrEmptySearch = errors.New("empty search string")
	ErrTimeout     = errors.New("expect timed out")
)

type ExpectSubprocess struct {
	Cmd          *exec.Cmd
	buf          *buffer
	outputBuffer []byte
}

type buffer struct {
	f       *os.File
	b       bytes.Buffer
	collect bool

	collection bytes.Buffer
}

func (buf *buffer) StartCollecting() {
	buf.collect = true
}

func (buf *buffer) StopCollecting() (result string) {
	result = string(buf.collection.Bytes())
	buf.collect = false
	buf.collection.Reset()
	return result
}

func (buf *buffer) Read(chunk []byte) (int, error) {
	nread := 0
	if buf.b.Len() > 0 {
		n, err := buf.b.Read(chunk)
		if err != nil {
			return n, err
		}
		if n == len(chunk) {
			return n, nil
		}
		nread = n
	}
	fn, err := buf.f.Read(chunk[nread:])
	return fn + nread, err
}

func (buf *buffer) ReadRune() (r rune, size int, err error) {
	l := buf.b.Len()

	chunk := make([]byte, utf8.UTFMax)
	if l > 0 {
		n, err := buf.b.Read(chunk)
		if err != nil {
			return 0, 0, err
		}
		if utf8.FullRune(chunk) {
			r, rL := utf8.DecodeRune(chunk)
			if n > rL {
				buf.PutBack(chunk[rL:n])
			}
			if buf.collect {
				buf.collection.WriteRune(r)
			}
			return r, rL, nil
		}
	}
	// else add bytes from the file, then try that
	for l < utf8.UTFMax {
		fn, err := buf.f.Read(chunk[l : l+1])
		if err != nil {
			return 0, 0, err
		}
		l = l + fn

		if utf8.FullRune(chunk) {
			r, rL := utf8.DecodeRune(chunk)
			if buf.collect {
				buf.collection.WriteRune(r)
			}
			return r, rL, nil
		}
	}
	return 0, 0, errors.New("File is not a valid UTF=8 encoding")
}

func (buf *buffer) PutBack(chunk []byte) {
	if len(chunk) == 0 {
		return
	}
	if buf.b.Len() == 0 {
		buf.b.Write(chunk)
		return
	}
	d := make([]byte, 0, len(chunk)+buf.b.Len())
	d = append(d, chunk...)
	d = append(d, buf.b.Bytes()...)
	buf.b.Reset()
	buf.b.Write(d)
}

func SpawnAtDirectory(command string, directory string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	expect.Cmd.Dir = directory
	return _start(expect)
}

func Command(command string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	return expect, nil
}

func (expect *ExpectSubprocess) Start() error {
	_, err := _start(expect)
	return err
}

func Spawn(command string) (*ExpectSubprocess, error) {
	expect, err := _spawn(command)
	if err != nil {
		return nil, err
	}
	return _start(expect)
}

func (expect *ExpectSubprocess) Close() error {
	if err := expect.Cmd.Process.Kill(); err != nil {
		return err
	}
	if err := expect.buf.f.Close(); err != nil {
		return err
	}
	return nil
}

func (expect *ExpectSubprocess) AsyncInteractChannels() (send chan string, receive chan string) {
	receive = make(chan string)
	send = make(chan string)

	go func() {
		for {
			str, err := expect.ReadLine()
			if err != nil {
				close(receive)
				return
			}
			receive <- str
		}
	}()

	go func() {
		for {
			select {
			case sendCommand, exists := <-send:
				{
					if !exists {
						return
					}
					err := expect.Send(sendCommand)
					if err != nil {
						receive <- "gexpect Error: " + err.Error()
						return
					}
				}
			}
		}
	}()

	return
}

func (expect *ExpectSubprocess) ExpectRegex(regex string) (bool, error) {
	return regexp.MatchReader(regex, expect.buf)
}

func (expect *ExpectSubprocess) expectRegexFind(regex string, output bool) ([]string, string, error) {
	re, err := regexp.Compile(regex)
	if err != nil {
		return nil, "", err
	}
	expect.buf.StartCollecting()
	pairs := re.FindReaderSubmatchIndex(expect.buf)
	stringIndexedInto := expect.buf.StopCollecting()
	l := len(pairs)
	numPairs := l / 2
	result := make([]string, numPairs)
	for i := 0; i < numPairs; i += 1 {
		result[i] = stringIndexedInto[pairs[i*2]:pairs[i*2+1]]
	}
	// convert indexes to strings

	if len(result) == 0 {
		err = fmt.Errorf("ExpectRegex didn't find regex '%v'.", regex)
	} else {
		// The number in pairs[1] is an index of a first
		// character outside the whole match
		putBackIdx := pairs[1]
		if len(stringIndexedInto) > putBackIdx {
			stringToPutBack := stringIndexedInto[putBackIdx:]
			stringIndexedInto = stringIndexedInto[:putBackIdx]
			expect.buf.PutBack([]byte(stringToPutBack))
		}
	}
	return result, stringIndexedInto, err
}

func (expect *ExpectSubprocess) expectTimeoutRegexFind(regex string, timeout time.Duration) (result []string, out string, err error) {
	t := make(chan bool)
	go func() {
		result, out, err = expect.ExpectRegexFindWithOutput(regex)
		t <- false
	}()
	go func() {
		time.Sleep(timeout)
		err = fmt.Errorf("ExpectRegex timed out after %v finding '%v'.\nOutput:\n%s", timeout, regex, expect.Collect())
		t <- true
	}()
	<-t
	return result, out, err
}

func (expect *ExpectSubprocess) ExpectRegexFind(regex string) ([]string, error) {
	result, _, err := expect.expectRegexFind(regex, false)
	return result, err
}

func (expect *ExpectSubprocess) ExpectTimeoutRegexFind(regex string, timeout time.Duration) ([]string, error) {
	result, _, err := expect.expectTimeoutRegexFind(regex, timeout)
	return result, err
}

func (expect *ExpectSubprocess) ExpectRegexFindWithOutput(regex string) ([]string, string, error) {
	return expect.expectRegexFind(regex, true)
}

func (expect *ExpectSubprocess) ExpectTimeoutRegexFindWithOutput(regex string, timeout time.Duration) ([]string, string, error) {
	return expect.expectTimeoutRegexFind(regex, timeout)
}

func buildKMPTable(searchString string) []int {
	pos := 2
	cnd := 0
	length := len(searchString)

	var table []int
	if length < 2 {
		length = 2
	}

	table = make([]int, length)
	table[0] = -1
	table[1] = 0

	for pos < len(searchString) {
		if searchString[pos-1] == searchString[cnd] {
			cnd += 1
			table[pos] = cnd
			pos += 1
		} else if cnd > 0 {
			cnd = table[cnd]
		} else {
			table[pos] = 0
			pos += 1
		}
	}
	return table
}

func (expect *ExpectSubprocess) ExpectTimeout(searchString string, timeout time.Duration) (e error) {
	result := make(chan error)
	go func() {
		result <- expect.Expect(searchString)
	}()
	select {
	case e = <-result:
	case <-time.After(timeout):
		e = fmt.Errorf("Expect timed out after %v waiting for '%v'.\nOutput:\n%s", timeout, searchString, expect.Collect())
	}
	return e
}

func (expect *ExpectSubprocess) Expect(searchString string) (e error) {
	target := len(searchString)
	if target < 1 {
		return ErrEmptySearch
	}
	chunk := make([]byte, target*2)
	if expect.outputBuffer != nil {
		expect.outputBuffer = expect.outputBuffer[0:]
	}
	m := 0
	i := 0
	// Build KMP Table
	table := buildKMPTable(searchString)

	for {
		n, err := expect.buf.Read(chunk)
		if err != nil {
			return err
		}
		if expect.outputBuffer != nil {
			expect.outputBuffer = append(expect.outputBuffer, chunk[:n]...)
		}
		offset := m + i
		for m+i-offset < n {
			if searchString[i] == chunk[m+i-offset] {
				i += 1
				if i == target {
					unreadIndex := m + i - offset
					if len(chunk) > unreadIndex {
						expect.buf.PutBack(chunk[unreadIndex:n])
					}
					return nil
				}
			} else {
				m += i - table[i]
				if table[i] > -1 {
					i = table[i]
				} else {
					i = 0
				}
			}
		}
	}
}

func (expect *ExpectSubprocess) Send(command string) error {
	_, err := io.WriteString(expect.buf.f, command)
	return err
}

func (expect *ExpectSubprocess) Capture() {
	if expect.outputBuffer == nil {
		expect.outputBuffer = make([]byte, 0)
	}
}

func (expect *ExpectSubprocess) Collect() []byte {
	collectOutput := make([]byte, len(expect.outputBuffer))
	copy(collectOutput, expect.outputBuffer)
	expect.outputBuffer = nil
	return collectOutput
}

func (expect *ExpectSubprocess) SendLine(command string) error {
	_, err := io.WriteString(expect.buf.f, command+"\r\n")
	return err
}

func (expect *ExpectSubprocess) Interact() {
	defer expect.Cmd.Wait()
	io.Copy(os.Stdout, &expect.buf.b)
	go io.Copy(os.Stdout, expect.buf.f)
	go io.Copy(expect.buf.f, os.Stdin)
}

func (expect *ExpectSubprocess) ReadUntil(delim byte) ([]byte, error) {
	join := make([]byte, 0, 512)
	chunk := make([]byte, 255)

	for {
		n, err := expect.buf.Read(chunk)

		for i := 0; i < n; i++ {
			if chunk[i] == delim {
				if len(chunk) > i+1 {
					expect.buf.PutBack(chunk[i+1:n])
				}
				return join, nil
			} else {
				join = append(join, chunk[i])
			}
		}

		if err != nil {
			return join, err
		}
	}
}

func (expect *ExpectSubprocess) Wait() error {
	return expect.Cmd.Wait()
}

func (expect *ExpectSubprocess) ReadLine() (string, error) {
	str, err := expect.ReadUntil('\n')
	return string(str), err
}

func _start(expect *ExpectSubprocess) (*ExpectSubprocess, error) {
	f, err := pty.Start(expect.Cmd)
	if err != nil {
		return nil, err
	}
	expect.buf.f = f

	return expect, nil
}

func _spawn(command string) (*ExpectSubprocess, error) {
	wrapper := new(ExpectSubprocess)

	wrapper.outputBuffer = nil

	splitArgs, err := shell.Split(command)
	if err != nil {
		return nil, err
	}
	numArguments := len(splitArgs) - 1
	if numArguments < 0 {
		return nil, errors.New("gexpect: No command given to spawn")
	}
	path, err := exec.LookPath(splitArgs[0])
	if err != nil {
		return nil, err
	}

	if numArguments >= 1 {
		wrapper.Cmd = exec.Command(path, splitArgs[1:]...)
	} else {
		wrapper.Cmd = exec.Command(path)
	}
	wrapper.buf = new(buffer)

	return wrapper, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/MichaelTJones/walk"
	"github.com/briandowns/spinner"
)

var (
	mutex      = sync.Mutex{}
	spinSet    = []string{"| ", "/ ", "- ", "\\ "}
	spin       = spinner.New(spinSet, 100*time.Millisecond)
	repodirs   = make(sort.StringSlice, 0)
	startdir   = "."
	alwaysshow = flag.Bool("a", false, "show directory name, even if there is no status")
	long       = flag.Bool("l", false, "show normal 'git status'")
	ignored    = flag.Bool("i", false, "show ignored files by adding the '--ignored' option")
	help       = flag.Bool("?", false, "show usage and options (this help)")
)

// init, makes sure we have a start directory
func init() {
	flag.Usage = usage
	flag.Parse()

	if *help {
		usage()
		os.Exit(0)
	}

	var dir = flag.Arg(0)
	if dir != "" {
		startdir = dir
	}
}

// usage
func usage() {
	fmt.Printf("\nGITSTAT (C) Copyright 2017 Erlend Johannessen\n")
	fmt.Printf("Finds all git repositories below the given path, and for each repository runs \"git status -s\".\n")
	fmt.Printf("Usage: gitstat [options] [dirname] \n")
	flag.PrintDefaults()
	fmt.Printf("\n")
}

// foreachEntry is called for each entry in the given directory
func foreachEntry(entryName string, f os.FileInfo, err error) error {
	if f == nil || !f.IsDir() || f.Name() != ".git" {
		return nil
	}

	// Getting the repo dir
	var dir = strings.Replace(entryName, string(os.PathSeparator)+".git", string(os.PathSeparator), 1)

	// edge case for current directory
	if dir == ".git" {
		dir = ""
	}

	var dirpath, patherr = filepath.Abs(ensureDir(dir))
	if patherr != nil {
		return nil
	}

	mutex.Lock()
	repodirs = append(repodirs, dirpath)
	mutex.Unlock()

	return nil
}

// ensureDir makes sure that the given directory is not empty
// if empty it is the current directory
func ensureDir(dir string) string {
	if dir == "" {
		return "."
	}

	return dir
}

// printStatus runs "git status -s" for the given directory
func printStatus(workdir string) {
	var gitdir = workdir + string(os.PathSeparator) + ".git"

	var args = []string{
		"--git-dir=" + gitdir,
		"--work-tree=" + ensureDir(workdir),
		"status",
	}

	if !*long {
		args = append(args, "-s")
	}

	if *ignored {
		args = append(args, "--ignored")
	}

	var cmd = exec.Command("git", args...)

	var r, w, _ = os.Pipe()
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	cmd.Run()

	w.Close()
	var status, _ = ioutil.ReadAll(r)

	if *alwaysshow || len(bytes.TrimSpace(status)) > 0 {
		fmt.Printf("----- %s -----\n", workdir)
		fmt.Printf("%v", string(status))
	}
}

// main
func main() {
	spin.Start()
	walk.Walk(startdir, foreachEntry)
	spin.Restart()
	spin.Stop()

	sort.Sort(repodirs)
	for _, wdir := range repodirs {
		printStatus(wdir)
	}
}
<commit_msg>minor changes<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/MichaelTJones/walk"
	"github.com/briandowns/spinner"
)

var (
	mutex      = sync.Mutex{}
	spinSet    = []string{"| ", "/ ", "- ", "\\ "}
	spin       = spinner.New(spinSet, 100*time.Millisecond)
	repodirs   = make(sort.StringSlice, 0)
	startdir   = "."
	help       = flag.Bool("?", false, "show usage and options (this help)")
	alwaysshow = flag.Bool("a", false, "show directory name, even if there is no status")
	ignored    = flag.Bool("i", false, "show ignored files by adding the '--ignored' option")
	long       = flag.Bool("l", false, "show normal 'git status'")
)

// init, makes sure we have a start directory
func init() {
	flag.Usage = usage
	flag.Parse()

	if *help {
		usage()
		os.Exit(0)
	}

	var dir = flag.Arg(0)
	if dir != "" {
		startdir = dir
	}
}

// usage
func usage() {
	fmt.Printf("\nGITSTAT (C) Copyright 2017 Erlend Johannessen\n")
	fmt.Printf("Finds all git repositories below the given path, and for each repository runs \"git status -s\".\n")
	fmt.Printf("Usage: gitstat [options] [dirname] \n")
	flag.PrintDefaults()
	fmt.Printf("\n")
}

// foreachEntry is called for each entry in the given directory
func foreachEntry(entryName string, f os.FileInfo, err error) error {
	if f == nil || !f.IsDir() || f.Name() != ".git" {
		return nil
	}

	// Getting the repo dir
	var dir = strings.Replace(entryName, string(os.PathSeparator)+".git", string(os.PathSeparator), 1)

	// edge case for current directory
	if dir == ".git" {
		dir = ""
	}

	var dirpath, patherr = filepath.Abs(ensureDir(dir))
	if patherr != nil {
		return nil
	}

	mutex.Lock()
	repodirs = append(repodirs, dirpath)
	mutex.Unlock()

	return nil
}

// ensureDir makes sure that the given directory is not empty
// if empty it is the current directory
func ensureDir(dir string) string {
	if dir == "" {
		return "."
	}

	return dir
}

// printStatus runs "git status -s" for the given directory
func printStatus(workdir string) {
	var gitdir = workdir + string(os.PathSeparator) + ".git"

	var args = []string{
		"--git-dir=" + gitdir,
		"--work-tree=" + ensureDir(workdir),
		"status",
	}

	if !*long {
		args = append(args, "-s")
	}

	if *ignored {
		args = append(args, "--ignored")
	}

	var cmd = exec.Command("git", args...)

	var r, w, _ = os.Pipe()
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	cmd.Run()

	w.Close()
	var status, _ = ioutil.ReadAll(r)

	if *alwaysshow || len(bytes.TrimSpace(status)) > 0 {
		fmt.Printf("----- %s -----\n", workdir)
		fmt.Printf("%v", string(status))
	}
}

// main
func main() {
	spin.Start()
	walk.Walk(startdir, foreachEntry)
	spin.Restart()
	spin.Stop()

	sort.Sort(repodirs)
	for _, wdir := range repodirs {
		printStatus(wdir)
	}
}
<|endoftext|>


<commit_before>package battery

import (
	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"text/template"

	"github.com/pkg/errors"
	"github.com/th3noname/i3blocks/blocks"
)

// Battery provides information about battery status
type Battery struct {
	Conf configuration
	Data data
}

type data struct {
	// either Full, Charging, Discharging
	Status string

	// in percent
	Power int

	// slice containing the remaining time (h:m:s)
	Time []string
}

type configuration struct {
	PrintTemplate string
	UrgentValue   int
	Color         string
	BatteryID     int
}

// New returns a instance of type Battery
func New() Battery {
	return Battery{
		Conf: configuration{
			PrintTemplate: "{{ .Status }} {{ .Power }} {{ .Time }}",
		},
	}
}

// Exec collects the information
func (b *Battery) Exec() error {
	out, err := exec.Command("acpi", "-b").CombinedOutput()
	if err != nil {
		return errors.Wrap(err, "command execution failed")
	}

	if len(out) == 0 {
		return errors.New("command returned empty")
	}

	return parseACPI(out)
}

// Print outputs a formatted string using PrintTemplate
func (b *Battery) Print() (blocks.Output, error) {
	t := template.New("b")

	t, err := t.Parse(b.Conf.PrintTemplate)
	if err != nil {
		return blocks.Output{}, errors.Wrap(err, "parsing template failed")
	}

	var out bytes.Buffer
	err = t.Execute(&out, b.Data)

	return blocks.Output{
			ShortText: out.String(),
			FullText:  out.String(),
			Urgent:    b.Data.Power <= b.Conf.UrgentValue,
			Color:     b.Conf.Color,
		},
		errors.Wrap(err, "executing template failed")
}

func (b *Battery) parseACPI(d []byte) error {
	batteryPrefix := []byte(fmt.Sprintf("Battery %d: ", b.Conf.BatteryID))

	batteries := bytes.Split(d, []byte("\n"))

	for _, battery := range batteries {
		i := bytes.Index(battery, batteryPrefix)
		if i == -1 {
			continue
		}

		parts := bytes.Split(battery[i + len(batteryPrefix):], []byte(","))

		for c, _ := range parts {
			parts[c] = bytes.TrimSpace(parts[c])
		}

		b.Data.Status = string(parts[0])

		b.Data.Power, err := strconv.Atoi(
			string(parts[1][:len(parts[1]) - 1]),
		)
		if err != nil {
			return errors.Wrap(err, "converting power value failed")
		}

		if len(parts) >= 3 {
			p.Data.Time = strings.Split(string(parts[2][:8]), ":")
		}

		return nil
	}

	return errors.New("specified battery id does not exist")
}
<commit_msg>fix var assignment<commit_after>package battery

import (
	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"text/template"

	"github.com/pkg/errors"
	"github.com/th3noname/i3blocks/blocks"
)

// Battery provides information about battery status
type Battery struct {
	Conf configuration
	Data data
}

type data struct {
	// either Full, Charging, Discharging
	Status string

	// in percent
	Power int

	// slice containing the remaining time (h:m:s)
	Time []string
}

type configuration struct {
	PrintTemplate string
	UrgentValue   int
	Color         string
	BatteryID     int
}

// New returns a instance of type Battery
func New() Battery {
	return Battery{
		Conf: configuration{
			PrintTemplate: "{{ .Status }} {{ .Power }} {{ .Time }}",
		},
	}
}

// Exec collects the information
func (b *Battery) Exec() error {
	out, err := exec.Command("acpi", "-b").CombinedOutput()
	if err != nil {
		return errors.Wrap(err, "command execution failed")
	}

	if len(out) == 0 {
		return errors.New("command returned empty")
	}

	return b.parseACPI(out)
}

// Print outputs a formatted string using PrintTemplate
func (b *Battery) Print() (blocks.Output, error) {
	t := template.New("b")

	t, err := t.Parse(b.Conf.PrintTemplate)
	if err != nil {
		return blocks.Output{}, errors.Wrap(err, "parsing template failed")
	}

	var out bytes.Buffer
	err = t.Execute(&out, b.Data)

	return blocks.Output{
			ShortText: out.String(),
			FullText:  out.String(),
			Urgent:    b.Data.Power <= b.Conf.UrgentValue,
			Color:     b.Conf.Color,
		},
		errors.Wrap(err, "executing template failed")
}

func (b *Battery) parseACPI(d []byte) error {
	batteryPrefix := []byte(fmt.Sprintf("Battery %d: ", b.Conf.BatteryID))

	batteries := bytes.Split(d, []byte("\n"))

	for _, battery := range batteries {
		i := bytes.Index(battery, batteryPrefix)
		if i == -1 {
			continue
		}

		parts := bytes.Split(battery[i + len(batteryPrefix):], []byte(","))

		for c, _ := range parts {
			parts[c] = bytes.TrimSpace(parts[c])
		}

		b.Data.Status = string(parts[0])

		var err error
		b.Data.Power, err = strconv.Atoi(
			string(parts[1][:len(parts[1]) - 1]),
		)
		if err != nil {
			return errors.Wrap(err, "converting power value failed")
		}

		if len(parts) >= 3 {
			b.Data.Time = strings.Split(string(parts[2][:8]), ":")
		}

		return nil
	}

	return errors.New("specified battery id does not exist")
}
<|endoftext|>


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

//
// Simple library for working with passwords in Go.
//
// All generated passwords are going to be a minimum of 8
// characters in length.
//

package GoPasswordUtilities

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"log"
	"regexp"
)

var (
	characters     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+,.?/:;{}[]~"
	passwordScores = map[int]string{
		0: "Horrible",
		1: "Weak",
		2: "Medium",
		3: "Strong",
		4: "Very Strong"}
)

type Password struct {
	Pass   string
	Length int
}

type PasswordComplexity struct {
	Length          int
	Score           int
	ContainsUpper   bool
	ContainsLower   bool
	ContainsNumber  bool
	ContainsSpecial bool
}

type SaltConf struct {
	Length int
}

// New is used when a user enters a password as well as the
// being called from the GeneratePassword function.
func New(password string) *Password {
	return &Password{Pass: password, Length: len(password)}
}

// GeneratePassword will generate and return a password as a string and as a
// byte slice of the given length.
func GeneratePassword(length int) *Password {
	passwordBuffer := new(bytes.Buffer)
	randBytes := make([]byte, length)
	if _, err := rand.Read(randBytes); err == nil {
		for j := 0; j < length; j++ {
			tmpIndex := int(randBytes[j]) % len(characters)
			char := characters[tmpIndex]
			passwordBuffer.WriteString(string(char))
		}
	}
	return New(passwordBuffer.String())
}

// GenerateVeryStrongPassword will generate a "Very Strong" password.
func GenerateVeryStrongPassword(length int) *Password {
	for {
		p := GeneratePassword(length)
		pc, err := ProcessPassword(p)
		if err != nil {
			log.Fatalln(err)
		}
		if pc.Score == 4 {
			return p
		}
	}
}

// getRandomBytes will generate random bytes.  This is for internal
// use in the library itself.
func getRandomBytes(length int) []byte {
	randomData := make([]byte, length)
	if _, err := rand.Read(randomData); err != nil {
		log.Fatalf("%v\n", err)
	}
	return randomData
}

// MD5 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) MD5(saltConf ...*SaltConf) ([16]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return md5.Sum([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return md5.Sum([]byte(p.Pass)), nil
}

// SHA256 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA256(saltConf ...*SaltConf) ([32]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return sha256.Sum256([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return sha256.Sum256([]byte(p.Pass)), nil
}

// SHA512 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA512(saltConf ...*SaltConf) ([64]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return sha512.Sum512([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return sha512.Sum512([]byte(p.Pass)), nil
}

// GetLength will provide the length of the password.  This method is
// being put on the password struct in case someone decides not to
// do a complexity check.
func (p *Password) GetLength() int {
	return p.Length
}

// ProcessPassword will parse the password and populate the PasswordComplexity struct.
func ProcessPassword(p *Password) (*PasswordComplexity, error) {
	c := &PasswordComplexity{}
	matchLower := regexp.MustCompile(`[a-z]`)
	matchUpper := regexp.MustCompile(`[A-Z]`)
	matchNumber := regexp.MustCompile(`[0-9]`)
	matchSpecial := regexp.MustCompile(`[\!\@\#\$\%\^\&\*\(\\\)\-_\=\+\,\.\?\/\:\;\{\}\[\]~]`)

	if p.Length < 8 {
		return nil, errors.New("Password isn't long enough for evaluation")
	} else {
		c.Length = p.Length
	}

	if matchLower.MatchString(p.Pass) {
		c.ContainsLower = true
		c.Score++
	}
	if matchUpper.MatchString(p.Pass) {
		c.ContainsUpper = true
		c.Score++
	}
	if matchNumber.MatchString(p.Pass) {
		c.ContainsNumber = true
		c.Score++
	}
	if matchSpecial.MatchString(p.Pass) {
		c.ContainsSpecial = true
		c.Score++
	}
	return c, nil
}

// GetScore will provide the score of the password.
func (c *PasswordComplexity) GetScore() int {
	return c.Score
}

// HasUpper indicates whether the password contains an upper case letter.
func (c *PasswordComplexity) HasUpper() bool {
	return c.ContainsUpper
}

// HasLower indicates whether the password contains a lower case letter.
func (c *PasswordComplexity) HasLower() bool {
	return c.ContainsLower
}

// HasNumber indicates whether the password contains a number.
func (c *PasswordComplexity) HasNumber() bool {
	return c.ContainsNumber
}

// HasSpecial indicates whether the password contains a special character.
func (c *PasswordComplexity) HasSpecial() bool {
	return c.ContainsSpecial
}

// ComplexityRating provides the rating for the password.
func (c *PasswordComplexity) ComplexityRating() string {
	return passwordScores[c.Score]
}
<commit_msg>Accommodating for lint run findings.<commit_after>// Copyright 2014 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
 Simple library for working with passwords in Go.

 All generated passwords are going to be a minimum of 8
 characters in length.
*/

package GoPasswordUtilities

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"log"
	"regexp"
)

var (
	characters     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+,.?/:;{}[]~"
	passwordScores = map[int]string{
		0: "Horrible",
		1: "Weak",
		2: "Medium",
		3: "Strong",
		4: "Very Strong"}
)

type Password struct {
	Pass   string
	Length int
}

type PasswordComplexity struct {
	Length          int
	Score           int
	ContainsUpper   bool
	ContainsLower   bool
	ContainsNumber  bool
	ContainsSpecial bool
}

type SaltConf struct {
	Length int
}

// New is used when a user enters a password as well as the
// being called from the GeneratePassword function.
func New(password string) *Password {
	return &Password{Pass: password, Length: len(password)}
}

// GeneratePassword will generate and return a password as a string and as a
// byte slice of the given length.
func GeneratePassword(length int) *Password {
	passwordBuffer := new(bytes.Buffer)
	randBytes := make([]byte, length)
	if _, err := rand.Read(randBytes); err == nil {
		for j := 0; j < length; j++ {
			tmpIndex := int(randBytes[j]) % len(characters)
			char := characters[tmpIndex]
			passwordBuffer.WriteString(string(char))
		}
	}
	return New(passwordBuffer.String())
}

// GenerateVeryStrongPassword will generate a "Very Strong" password.
func GenerateVeryStrongPassword(length int) *Password {
	for {
		p := GeneratePassword(length)
		pc, err := ProcessPassword(p)
		if err != nil {
			log.Fatalln(err)
		}
		if pc.Score == 4 {
			return p
		}
	}
}

// getRandomBytes will generate random bytes.  This is for internal
// use in the library itself.
func getRandomBytes(length int) []byte {
	randomData := make([]byte, length)
	if _, err := rand.Read(randomData); err != nil {
		log.Fatalf("%v\n", err)
	}
	return randomData
}

// MD5 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) MD5(saltConf ...*SaltConf) ([16]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return md5.Sum([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return md5.Sum([]byte(p.Pass)), nil
}

// SHA256 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA256(saltConf ...*SaltConf) ([32]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return sha256.Sum256([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return sha256.Sum256([]byte(p.Pass)), nil
}

// SHA512 sum for the given password.  If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA512(saltConf ...*SaltConf) ([64]byte, []byte) {
	if len(saltConf) > 0 {
		var saltLength int
		for _, i := range saltConf[0:] {
			saltLength = i.Length
		}
		salt := getRandomBytes(saltLength)
		return sha512.Sum512([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
	}
	return sha512.Sum512([]byte(p.Pass)), nil
}

// GetLength will provide the length of the password.  This method is
// being put on the password struct in case someone decides not to
// do a complexity check.
func (p *Password) GetLength() int {
	return p.Length
}

// ProcessPassword will parse the password and populate the PasswordComplexity struct.
func ProcessPassword(p *Password) (*PasswordComplexity, error) {
	c := &PasswordComplexity{}
	matchLower := regexp.MustCompile(`[a-z]`)
	matchUpper := regexp.MustCompile(`[A-Z]`)
	matchNumber := regexp.MustCompile(`[0-9]`)
	matchSpecial := regexp.MustCompile(`[\!\@\#\$\%\^\&\*\(\\\)\-_\=\+\,\.\?\/\:\;\{\}\[\]~]`)

	if p.Length < 8 {
		return nil, errors.New("Password isn't long enough for evaluation")
	} else {
		c.Length = p.Length
	}

	if matchLower.MatchString(p.Pass) {
		c.ContainsLower = true
		c.Score++
	}
	if matchUpper.MatchString(p.Pass) {
		c.ContainsUpper = true
		c.Score++
	}
	if matchNumber.MatchString(p.Pass) {
		c.ContainsNumber = true
		c.Score++
	}
	if matchSpecial.MatchString(p.Pass) {
		c.ContainsSpecial = true
		c.Score++
	}
	return c, nil
}

// GetScore will provide the score of the password.
func (c *PasswordComplexity) GetScore() int {
	return c.Score
}

// HasUpper indicates whether the password contains an upper case letter.
func (c *PasswordComplexity) HasUpper() bool {
	return c.ContainsUpper
}

// HasLower indicates whether the password contains a lower case letter.
func (c *PasswordComplexity) HasLower() bool {
	return c.ContainsLower
}

// HasNumber indicates whether the password contains a number.
func (c *PasswordComplexity) HasNumber() bool {
	return c.ContainsNumber
}

// HasSpecial indicates whether the password contains a special character.
func (c *PasswordComplexity) HasSpecial() bool {
	return c.ContainsSpecial
}

// ComplexityRating provides the rating for the password.
func (c *PasswordComplexity) ComplexityRating() string {
	return passwordScores[c.Score]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/getlantern/appdir"
	"github.com/getlantern/launcher"
	"github.com/getlantern/yaml"

	"github.com/getlantern/flashlight/ui"
)

const (
	messageType = `settings`
)

var (
	service    *ui.Service
	settings   *Settings
	httpClient *http.Client
	path       = filepath.Join(appdir.General("Lantern"), "settings.yaml")
	once       = &sync.Once{}
)

// Settings is a struct of all settings unique to this particular Lantern instance.
type Settings struct {
	DeviceID  string `json:"deviceID,omitempty"`
	UserID    int    `json:"userID,omitempty"`
	UserToken string `json:"userToken,omitempty"`

	Version      string `json:"version"`
	BuildDate    string `json:"buildDate"`
	RevisionDate string `json:"revisionDate"`
	AutoReport   bool   `json:"autoReport"`
	AutoLaunch   bool   `json:"autoLaunch"`
	ProxyAll     bool   `json:"proxyAll"`
	SystemProxy  bool   `json:"systemProxy"`

	sync.RWMutex `json:"-" yaml:"-"`
}

// Load loads the initial settings at startup, either from disk or using defaults.
func LoadSettings(version, revisionDate, buildDate string) *Settings {
	log.Debug("Loading settings")
	// Create default settings that may or may not be overridden from an existing file
	// on disk.
	settings = &Settings{
		AutoReport:  true,
		AutoLaunch:  true,
		ProxyAll:    false,
		SystemProxy: true,
	}

	// Use settings from disk if they're available.
	if bytes, err := ioutil.ReadFile(path); err != nil {
		log.Debugf("Could not read file %v", err)
	} else if err := yaml.Unmarshal(bytes, settings); err != nil {
		log.Errorf("Could not load yaml %v", err)
		// Just keep going with the original settings not from disk.
	} else {
		log.Debugf("Loaded settings from %v", path)
	}

	if settings.AutoLaunch {
		launcher.CreateLaunchFile(settings.AutoLaunch)
	}
	// always override below 3 attributes as they are not meant to be persisted across versions
	settings.Version = version
	settings.BuildDate = buildDate
	settings.RevisionDate = revisionDate

	// Only configure the UI once. This will typically be the case in the normal
	// application flow, but tests might call Load twice, for example, which we
	// want to allow.
	once.Do(func() {
		err := settings.start()
		if err != nil {
			log.Errorf("Unable to register settings service: %q", err)
			return
		}
		go settings.read()
	})
	return settings
}

// start the settings service that synchronizes Lantern's configuration with every UI client
func (s *Settings) start() error {
	var err error

	ui.PreferProxiedUI(s.SystemProxy)
	helloFn := func(write func(interface{}) error) error {
		log.Debugf("Sending Lantern settings to new client")
		s.Lock()
		defer s.Unlock()
		return write(s)
	}
	service, err = ui.Register(messageType, nil, helloFn)
	return err
}

func (s *Settings) read() {
	log.Debugf("Reading settings messages!!")
	for message := range service.In {
		log.Debugf("Read settings message!! %v", message)

		// We're using a map here because we want to know when the user sends a
		// false value.
		var data map[string]interface{}
		var decoded bool

		if data, decoded = (message).(map[string]interface{}); !decoded {
			continue
		}

		var v, ok bool

		if v, ok = data["autoReport"].(bool); ok {
			s.SetAutoReport(v)
		}

		if v, ok = data["proxyAll"].(bool); ok {
			s.SetProxyAll(v)
		}

		if v, ok = data["autoLaunch"].(bool); ok {
			s.SetAutoLaunch(v)
		}

		if v, ok = data["systemProxy"].(bool); ok {
			s.SetSystemProxy(v)
		}

		if data["userID"] != nil {
			// This is unmarshaled into a float64, I'm am converting it to string and
			// then to float32 to catch the case when this is a float32.
			if id, err := strconv.Atoi(fmt.Sprintf("%v", data["userID"])); err == nil {
				s.SetUserID(id)
			}
		}

		if token, ok := data["token"].(string); ok {
			s.SetToken(token)
		}

		if deviceID, ok := data["deviceID"].(string); ok {
			s.SetDeviceID(deviceID)
		}

		service.Out <- s
	}
}

// Save saves settings to disk.
func (s *Settings) save() {
	log.Debug("Saving settings")
	s.Lock()
	defer s.Unlock()
	if bytes, err := yaml.Marshal(s); err != nil {
		log.Errorf("Could not create yaml from settings %v", err)
	} else if err := ioutil.WriteFile(path, bytes, 0644); err != nil {
		log.Errorf("Could not write settings file %v", err)
	} else {
		log.Debugf("Saved settings to %s with contents %v", path, string(bytes))
	}
}

// GetProxyAll returns whether or not to proxy all traffic.
func (s *Settings) GetProxyAll() bool {
	s.RLock()
	defer s.RUnlock()
	return s.ProxyAll
}

// SetProxyAll sets whether or not to proxy all traffic.
func (s *Settings) SetProxyAll(proxyAll bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.ProxyAll = proxyAll
	// Cycle the PAC file so that browser picks up changes
	cyclePAC()
}

// IsAutoReport returns whether or not to auto-report debugging and analytics data.
func (s *Settings) IsAutoReport() bool {
	s.RLock()
	defer s.RUnlock()
	return s.AutoReport
}

// SetAutoReport sets whether or not to auto-report debugging and analytics data.
func (s *Settings) SetAutoReport(auto bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.AutoReport = auto
}

// SetAutoLaunch sets whether or not to auto-launch Lantern on system startup.
func (s *Settings) SetAutoLaunch(auto bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.AutoLaunch = auto
	go launcher.CreateLaunchFile(auto)
}

// GetSystemProxy returns whether or not to set system proxy when lantern starts
func (s *Settings) GetSystemProxy() bool {
	s.RLock()
	defer s.RUnlock()
	return s.SystemProxy
}

// SetDeviceID sets the device ID
func (s *Settings) SetDeviceID(deviceID string) {
	s.Lock()
	defer s.unlockAndSave()
	s.DeviceID = deviceID
}

// SetToken sets the user token
func (s *Settings) SetToken(token string) {
	s.Lock()
	defer s.unlockAndSave()
	s.UserToken = token
}

// GetToken returns the user token
func (s *Settings) GetToken() string {
	s.RLock()
	defer s.RUnlock()
	return s.UserToken
}

// SetUserID sets the user ID
func (s *Settings) SetUserID(id int) {
	s.Lock()
	defer s.unlockAndSave()
	s.UserID = id
}

// GetUserID returns the user ID
func (s *Settings) GetUserID() int {
	s.RLock()
	defer s.RUnlock()
	return s.UserID
}

// SetSystemProxy sets whether or not to set system proxy when lantern starts
func (s *Settings) SetSystemProxy(enable bool) {
	s.Lock()
	defer s.unlockAndSave()
	changed := enable != s.SystemProxy
	s.SystemProxy = enable
	if changed {
		if enable {
			pacOn()
		} else {
			pacOff()
		}
		preferredUIAddr, addrChanged := ui.PreferProxiedUI(enable)
		if !enable && addrChanged {
			log.Debugf("System proxying disabled, redirect UI to: %v", preferredUIAddr)
			service.Out <- map[string]string{"redirectTo": preferredUIAddr}
		}
	}
}

// unlockAndSave releases the lock on writing to settings and then saves settings.
func (s *Settings) unlockAndSave() {
	// Note locks in go aren't reentrant, so we need to unlock before save locks again.
	s.Unlock()
	s.save()
}
<commit_msg>not persist version to settings.yaml resolve #3819<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/getlantern/appdir"
	"github.com/getlantern/launcher"
	"github.com/getlantern/yaml"

	"github.com/getlantern/flashlight/ui"
)

const (
	messageType = `settings`
)

var (
	service    *ui.Service
	settings   *Settings
	httpClient *http.Client
	path       = filepath.Join(appdir.General("Lantern"), "settings.yaml")
	once       = &sync.Once{}
)

// Settings is a struct of all settings unique to this particular Lantern instance.
type Settings struct {
	DeviceID  string `json:"deviceID,omitempty"`
	UserID    int    `json:"userID,omitempty"`
	UserToken string `json:"userToken,omitempty"`

	AutoReport  bool `json:"autoReport"`
	AutoLaunch  bool `json:"autoLaunch"`
	ProxyAll    bool `json:"proxyAll"`
	SystemProxy bool `json:"systemProxy"`

	Version      string `json:"version" yaml:"-"`
	BuildDate    string `json:"buildDate" yaml:"-"`
	RevisionDate string `json:"revisionDate" yaml:"-"`

	sync.RWMutex `json:"-" yaml:"-"`
}

// Load loads the initial settings at startup, either from disk or using defaults.
func LoadSettings(version, revisionDate, buildDate string) *Settings {
	log.Debug("Loading settings")
	// Create default settings that may or may not be overridden from an existing file
	// on disk.
	settings = &Settings{
		AutoReport:  true,
		AutoLaunch:  true,
		ProxyAll:    false,
		SystemProxy: true,
	}

	// Use settings from disk if they're available.
	if bytes, err := ioutil.ReadFile(path); err != nil {
		log.Debugf("Could not read file %v", err)
	} else if err := yaml.Unmarshal(bytes, settings); err != nil {
		log.Errorf("Could not load yaml %v", err)
		// Just keep going with the original settings not from disk.
	} else {
		log.Debugf("Loaded settings from %v", path)
	}

	if settings.AutoLaunch {
		launcher.CreateLaunchFile(settings.AutoLaunch)
	}
	// always override below 3 attributes as they are not meant to be persisted across versions
	settings.Version = version
	settings.BuildDate = buildDate
	settings.RevisionDate = revisionDate

	// Only configure the UI once. This will typically be the case in the normal
	// application flow, but tests might call Load twice, for example, which we
	// want to allow.
	once.Do(func() {
		err := settings.start()
		if err != nil {
			log.Errorf("Unable to register settings service: %q", err)
			return
		}
		go settings.read()
	})
	return settings
}

// start the settings service that synchronizes Lantern's configuration with every UI client
func (s *Settings) start() error {
	var err error

	ui.PreferProxiedUI(s.SystemProxy)
	helloFn := func(write func(interface{}) error) error {
		log.Debugf("Sending Lantern settings to new client")
		s.Lock()
		defer s.Unlock()
		return write(s)
	}
	service, err = ui.Register(messageType, nil, helloFn)
	return err
}

func (s *Settings) read() {
	log.Debugf("Reading settings messages!!")
	for message := range service.In {
		log.Debugf("Read settings message!! %v", message)

		// We're using a map here because we want to know when the user sends a
		// false value.
		var data map[string]interface{}
		var decoded bool

		if data, decoded = (message).(map[string]interface{}); !decoded {
			continue
		}

		var v, ok bool

		if v, ok = data["autoReport"].(bool); ok {
			s.SetAutoReport(v)
		}

		if v, ok = data["proxyAll"].(bool); ok {
			s.SetProxyAll(v)
		}

		if v, ok = data["autoLaunch"].(bool); ok {
			s.SetAutoLaunch(v)
		}

		if v, ok = data["systemProxy"].(bool); ok {
			s.SetSystemProxy(v)
		}

		if data["userID"] != nil {
			// This is unmarshaled into a float64, I'm am converting it to string and
			// then to float32 to catch the case when this is a float32.
			if id, err := strconv.Atoi(fmt.Sprintf("%v", data["userID"])); err == nil {
				s.SetUserID(id)
			}
		}

		if token, ok := data["token"].(string); ok {
			s.SetToken(token)
		}

		if deviceID, ok := data["deviceID"].(string); ok {
			s.SetDeviceID(deviceID)
		}

		service.Out <- s
	}
}

// Save saves settings to disk.
func (s *Settings) save() {
	log.Debug("Saving settings")
	s.Lock()
	defer s.Unlock()
	if bytes, err := yaml.Marshal(s); err != nil {
		log.Errorf("Could not create yaml from settings %v", err)
	} else if err := ioutil.WriteFile(path, bytes, 0644); err != nil {
		log.Errorf("Could not write settings file %v", err)
	} else {
		log.Debugf("Saved settings to %s with contents %v", path, string(bytes))
	}
}

// GetProxyAll returns whether or not to proxy all traffic.
func (s *Settings) GetProxyAll() bool {
	s.RLock()
	defer s.RUnlock()
	return s.ProxyAll
}

// SetProxyAll sets whether or not to proxy all traffic.
func (s *Settings) SetProxyAll(proxyAll bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.ProxyAll = proxyAll
	// Cycle the PAC file so that browser picks up changes
	cyclePAC()
}

// IsAutoReport returns whether or not to auto-report debugging and analytics data.
func (s *Settings) IsAutoReport() bool {
	s.RLock()
	defer s.RUnlock()
	return s.AutoReport
}

// SetAutoReport sets whether or not to auto-report debugging and analytics data.
func (s *Settings) SetAutoReport(auto bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.AutoReport = auto
}

// SetAutoLaunch sets whether or not to auto-launch Lantern on system startup.
func (s *Settings) SetAutoLaunch(auto bool) {
	s.Lock()
	defer s.unlockAndSave()
	s.AutoLaunch = auto
	go launcher.CreateLaunchFile(auto)
}

// GetSystemProxy returns whether or not to set system proxy when lantern starts
func (s *Settings) GetSystemProxy() bool {
	s.RLock()
	defer s.RUnlock()
	return s.SystemProxy
}

// SetDeviceID sets the device ID
func (s *Settings) SetDeviceID(deviceID string) {
	s.Lock()
	defer s.unlockAndSave()
	s.DeviceID = deviceID
}

// SetToken sets the user token
func (s *Settings) SetToken(token string) {
	s.Lock()
	defer s.unlockAndSave()
	s.UserToken = token
}

// GetToken returns the user token
func (s *Settings) GetToken() string {
	s.RLock()
	defer s.RUnlock()
	return s.UserToken
}

// SetUserID sets the user ID
func (s *Settings) SetUserID(id int) {
	s.Lock()
	defer s.unlockAndSave()
	s.UserID = id
}

// GetUserID returns the user ID
func (s *Settings) GetUserID() int {
	s.RLock()
	defer s.RUnlock()
	return s.UserID
}

// SetSystemProxy sets whether or not to set system proxy when lantern starts
func (s *Settings) SetSystemProxy(enable bool) {
	s.Lock()
	defer s.unlockAndSave()
	changed := enable != s.SystemProxy
	s.SystemProxy = enable
	if changed {
		if enable {
			pacOn()
		} else {
			pacOff()
		}
		preferredUIAddr, addrChanged := ui.PreferProxiedUI(enable)
		if !enable && addrChanged {
			log.Debugf("System proxying disabled, redirect UI to: %v", preferredUIAddr)
			service.Out <- map[string]string{"redirectTo": preferredUIAddr}
		}
	}
}

// unlockAndSave releases the lock on writing to settings and then saves settings.
func (s *Settings) unlockAndSave() {
	// Note locks in go aren't reentrant, so we need to unlock before save locks again.
	s.Unlock()
	s.save()
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/go-igdman/igdman"
	"github.com/getlantern/golog"
	"github.com/getlantern/nattywad"
	"github.com/getlantern/waddell"

	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/nattest"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
)

const (
	PortmapFailure = 50
)

var (
	log               = golog.LoggerFor("flashlight.server")
	registerPeriod    = 5 * time.Minute
	frontingProviders = map[string]func(*http.Request) bool{
		// WARNING: If you add a provider here, keep in mind that Go's http
		// library normalizes all header names so the first letter of every
		// dash-separated "word" is uppercase while all others are lowercase.
		// Also, try and check more than one header to lean on the safe side.
		"cloudflare": func(req *http.Request) bool {
			return hasHeader(req, "Cf-Connecting-Ip") || hasHeader(req, "Cf-Ipcountry") || hasHeader(req, "Cf-Ray") || hasHeader(req, "Cf-Visitor")
		},
		"cloudfront": func(req *http.Request) bool {
			return hasHeader(req, "X-Amz-Cf-Id") || headerMatches(req, "User-Agent", "Amazon Cloudfront")
		},
	}
)

func headerMatches(req *http.Request, name string, value string) bool {
	h := req.Header[name]
	return len(h) == 1 && h[0] == value
}

func hasHeader(req *http.Request, name string) bool {
	return req.Header[name] != nil
}

type Server struct {
	// Addr: listen address in form of host:port
	Addr string

	// HostFn: Function mapping a http.Request to a FQDN that is guaranteed to
	// hit this server through the same front as the request.
	HostFn func(*http.Request) string

	// ReadTimeout: (optional) timeout for read ops
	ReadTimeout time.Duration

	// WriteTimeout: (optional) timeout for write ops
	WriteTimeout time.Duration

	CertContext                *fronted.CertContext // context for certificate management
	AllowNonGlobalDestinations bool                 // if true, requests to LAN, Loopback, etc. will be allowed
	AllowedPorts               []int                // if specified, only connections to these ports will be allowed

	waddellClient  *waddell.Client
	nattywadServer *nattywad.Server
	cfg            *ServerConfig
	cfgMutex       sync.RWMutex
}

func (server *Server) Configure(newCfg *ServerConfig) {
	server.cfgMutex.Lock()
	defer server.cfgMutex.Unlock()

	oldCfg := server.cfg

	log.Debug("Server.Configure() called")
	if oldCfg != nil && reflect.DeepEqual(oldCfg, newCfg) {
		log.Debugf("Server configuration unchanged")
		return
	}

	if oldCfg == nil || newCfg.Portmap != oldCfg.Portmap {
		// Portmap changed
		if oldCfg != nil && oldCfg.Portmap > 0 {
			log.Debugf("Attempting to unmap old external port %d", oldCfg.Portmap)
			err := unmapPort(oldCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to unmap old external port: %s", err)
			}
			log.Debugf("Unmapped old external port %d", oldCfg.Portmap)
		}

		if newCfg.Portmap > 0 {
			log.Debugf("Attempting to map new external port %d", newCfg.Portmap)
			err := mapPort(server.Addr, newCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to map new external port: %s", err)
				os.Exit(PortmapFailure)
			}
			log.Debugf("Mapped new external port %d", newCfg.Portmap)
		}
	}

	nattywadIsEnabled := newCfg.WaddellAddr != ""
	nattywadWasEnabled := server.nattywadServer != nil
	waddellAddrChanged := oldCfg == nil && newCfg.WaddellAddr != "" || oldCfg != nil && oldCfg.WaddellAddr != newCfg.WaddellAddr

	if waddellAddrChanged {
		if nattywadWasEnabled {
			server.stopNattywad()
		}
		if nattywadIsEnabled {
			server.startNattywad(newCfg.WaddellAddr)
		}
	}

	if newCfg.FrontFQDNs != nil {
		server.HostFn = hostFn(newCfg.FrontFQDNs)
	}
	server.cfg = newCfg
}

func (server *Server) ListenAndServe() error {

	fs := &fronted.Server{
		Addr:                       server.Addr,
		HostFn:                     server.HostFn,
		ReadTimeout:                server.ReadTimeout,
		WriteTimeout:               server.WriteTimeout,
		CertContext:                server.CertContext,
		AllowNonGlobalDestinations: server.AllowNonGlobalDestinations,
		AllowedPorts:               server.AllowedPorts,
	}

	if server.cfg.Unencrypted {
		log.Debug("Running in unencrypted mode")
		fs.CertContext = nil
	}

	// Add callbacks to track bytes given
	fs.OnBytesReceived = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesReceived(ip, bytes)
	}
	fs.OnBytesSent = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesSent(ip, bytes)
	}

	l, err := fs.Listen()
	if err != nil {
		return fmt.Errorf("Unable to listen at %s: %s", server.Addr, err)
	}

	go server.register()

	return fs.Serve(l)
}

func (server *Server) register() {
	for {
		server.cfgMutex.RLock()
		baseUrl := server.cfg.RegisterAt
		server.cfgMutex.RUnlock()
		if baseUrl != "" {
			if globals.InstanceId == "" {
				log.Error("Unable to register server because no InstanceId is configured")
			} else {
				log.Debugf("Registering server at %v", baseUrl)
				registerUrl := baseUrl + "/register"
				vals := url.Values{
					"name": []string{globals.InstanceId},
					"port": []string{"443"},
				}
				resp, err := http.PostForm(registerUrl, vals)
				if err != nil {
					log.Errorf("Unable to register at %v: %v", registerUrl, err)
					return
				} else if resp.StatusCode != 200 {
					bodyString, _ := ioutil.ReadAll(resp.Body)
					log.Errorf("Unexpected response status registering at %v: %d    %v", registerUrl, resp.StatusCode, string(bodyString))
				} else {
					log.Debugf("Successfully registered server at %v", registerUrl)
				}
				resp.Body.Close()
				time.Sleep(registerPeriod)
			}
		}
	}
}

func (server *Server) startNattywad(waddellAddr string) {
	log.Debugf("Connecting to waddell at: %s", waddellAddr)
	var err error
	server.waddellClient, err = waddell.NewClient(&waddell.ClientConfig{
		Dial: func() (net.Conn, error) {
			return net.Dial("tcp", waddellAddr)
		},
		ServerCert:        globals.WaddellCert,
		ReconnectAttempts: 10,
		OnId: func(id waddell.PeerId) {
			log.Debugf("Connected to Waddell!! Id is: %s", id)
		},
	})
	if err != nil {
		log.Errorf("Unable to connect to waddell: %s", err)
		server.waddellClient = nil
		return
	}
	server.nattywadServer = &nattywad.Server{
		Client: server.waddellClient,
		OnSuccess: func(local *net.UDPAddr, remote *net.UDPAddr) bool {
			err := nattest.Serve(local)
			if err != nil {
				log.Error(err.Error())
				return false
			}
			return true
		},
	}
	server.nattywadServer.Start()
}

func (server *Server) stopNattywad() {
	log.Debug("Stopping nattywad server")
	server.nattywadServer.Stop()
	server.nattywadServer = nil
	log.Debug("Stopping waddell client")
	server.waddellClient.Close()
	server.waddellClient = nil
}

func mapPort(addr string, port int) error {
	internalIP, internalPortString, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	internalPort, err := strconv.Atoi(internalPortString)
	if err != nil {
		return fmt.Errorf("Unable to parse local port: ")
	}

	if internalIP == "" {
		internalIP, err = determineInternalIP()
		if err != nil {
			return fmt.Errorf("Unable to determine internal IP: %s", err)
		}
	}

	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	err = igd.AddPortMapping(igdman.TCP, internalIP, internalPort, port, 0)
	if err != nil {
		return fmt.Errorf("Unable to map port with igdman %d: %s", port, err)
	}

	return nil
}

func unmapPort(port int) error {
	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	if err != nil {
		return fmt.Errorf("Unable to unmap port with igdman %d: %s", port, err)
	}

	return nil
}

// determineInternalIP determines the internal IP to use for mapping ports. It
// does this by dialing a website on the public Internet and then finding out
// the LocalAddr for the corresponding connection. This gives us an interface
// that we know has Internet access, which makes it suitable for port mapping.
func determineInternalIP() (string, error) {
	conn, err := net.DialTimeout("tcp", "s3.amazonaws.com:443", 20*time.Second)
	if err != nil {
		return "", fmt.Errorf("Unable to determine local IP: %s", err)
	}
	defer conn.Close()
	host, _, err := net.SplitHostPort(conn.LocalAddr().String())
	return host, err
}

func onBytesGiven(destAddr string, req *http.Request, bytes int64) {
	host, port, _ := net.SplitHostPort(destAddr)
	if port == "" {
		port = "0"
	}

	given := statreporter.CountryDim().
		And("flserver", globals.InstanceId).
		And("destport", port)
	given.Increment("bytesGiven").Add(bytes)
	given.Increment("bytesGivenByFlashlight").Add(bytes)

	clientCountry := req.Header.Get("Cf-Ipcountry")
	if clientCountry != "" {
		givenTo := statreporter.Country(clientCountry)
		givenTo.Increment("bytesGivenTo").Add(bytes)
		givenTo.Increment("bytesGivenToByFlashlight").Add(bytes)
		givenTo.Member("distinctDestHosts", host)

		clientIp := req.Header.Get("X-Forwarded-For")
		if clientIp != "" {
			// clientIp may contain multiple ips, use the first
			ips := strings.Split(clientIp, ",")
			clientIp := strings.TrimSpace(ips[0])
			givenTo.Member("distinctClients", clientIp)
		}

	}
}

func hostFn(fqdns map[string]string) func(*http.Request) string {
	// We prefer to use the fronting provider through which we have been
	// reached, because we expect that to be unblocked, but if something goes
	// wrong (e.g. in old give mode peers) we'll use just any configured host.
	return func(req *http.Request) string {
		for provider, fn := range frontingProviders {
			if fn(req) {
				fqdn, found := fqdns[provider]
				if found {
					return fqdn
				}
			}
		}
		// We don't know about this provider or we don't have a fqdn for it...
		// The best we can do is provide *some* FQDN through which we hope we
		// can be reached.
		log.Debugf("Falling back to just any FQDN")
		for _, fqdn := range fqdns {
			return fqdn
		}
		// We don't know of any fqdns.  This will be treated like a null
		// hostFn.
		return ""
	}
}
<commit_msg>slightly less strict header matching<commit_after>package server

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/getlantern/fronted"
	"github.com/getlantern/go-igdman/igdman"
	"github.com/getlantern/golog"
	"github.com/getlantern/nattywad"
	"github.com/getlantern/waddell"

	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/nattest"
	"github.com/getlantern/flashlight/statreporter"
	"github.com/getlantern/flashlight/statserver"
)

const (
	PortmapFailure = 50
)

var (
	log               = golog.LoggerFor("flashlight.server")
	registerPeriod    = 5 * time.Minute
	frontingProviders = map[string]func(*http.Request) bool{
		// WARNING: If you add a provider here, keep in mind that Go's http
		// library normalizes all header names so the first letter of every
		// dash-separated "word" is uppercase while all others are lowercase.
		// Also, try and check more than one header to lean on the safe side.
		"cloudflare": func(req *http.Request) bool {
			return hasHeader(req, "Cf-Connecting-Ip") || hasHeader(req, "Cf-Ipcountry") || hasHeader(req, "Cf-Ray") || hasHeader(req, "Cf-Visitor")
		},
		"cloudfront": func(req *http.Request) bool {
			return hasHeader(req, "X-Amz-Cf-Id") || headerMatches(req, "User-Agent", "Amazon Cloudfront")
		},
	}
)

func headerMatches(req *http.Request, name string, value string) bool {
	for _, entry := range req.Header[name] {
		if entry == value {
			return true
		}
	}
	return false
}

func hasHeader(req *http.Request, name string) bool {
	return req.Header[name] != nil
}

type Server struct {
	// Addr: listen address in form of host:port
	Addr string

	// HostFn: Function mapping a http.Request to a FQDN that is guaranteed to
	// hit this server through the same front as the request.
	HostFn func(*http.Request) string

	// ReadTimeout: (optional) timeout for read ops
	ReadTimeout time.Duration

	// WriteTimeout: (optional) timeout for write ops
	WriteTimeout time.Duration

	CertContext                *fronted.CertContext // context for certificate management
	AllowNonGlobalDestinations bool                 // if true, requests to LAN, Loopback, etc. will be allowed
	AllowedPorts               []int                // if specified, only connections to these ports will be allowed

	waddellClient  *waddell.Client
	nattywadServer *nattywad.Server
	cfg            *ServerConfig
	cfgMutex       sync.RWMutex
}

func (server *Server) Configure(newCfg *ServerConfig) {
	server.cfgMutex.Lock()
	defer server.cfgMutex.Unlock()

	oldCfg := server.cfg

	log.Debug("Server.Configure() called")
	if oldCfg != nil && reflect.DeepEqual(oldCfg, newCfg) {
		log.Debugf("Server configuration unchanged")
		return
	}

	if oldCfg == nil || newCfg.Portmap != oldCfg.Portmap {
		// Portmap changed
		if oldCfg != nil && oldCfg.Portmap > 0 {
			log.Debugf("Attempting to unmap old external port %d", oldCfg.Portmap)
			err := unmapPort(oldCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to unmap old external port: %s", err)
			}
			log.Debugf("Unmapped old external port %d", oldCfg.Portmap)
		}

		if newCfg.Portmap > 0 {
			log.Debugf("Attempting to map new external port %d", newCfg.Portmap)
			err := mapPort(server.Addr, newCfg.Portmap)
			if err != nil {
				log.Errorf("Unable to map new external port: %s", err)
				os.Exit(PortmapFailure)
			}
			log.Debugf("Mapped new external port %d", newCfg.Portmap)
		}
	}

	nattywadIsEnabled := newCfg.WaddellAddr != ""
	nattywadWasEnabled := server.nattywadServer != nil
	waddellAddrChanged := oldCfg == nil && newCfg.WaddellAddr != "" || oldCfg != nil && oldCfg.WaddellAddr != newCfg.WaddellAddr

	if waddellAddrChanged {
		if nattywadWasEnabled {
			server.stopNattywad()
		}
		if nattywadIsEnabled {
			server.startNattywad(newCfg.WaddellAddr)
		}
	}

	if newCfg.FrontFQDNs != nil {
		server.HostFn = hostFn(newCfg.FrontFQDNs)
	}
	server.cfg = newCfg
}

func (server *Server) ListenAndServe() error {

	fs := &fronted.Server{
		Addr:                       server.Addr,
		HostFn:                     server.HostFn,
		ReadTimeout:                server.ReadTimeout,
		WriteTimeout:               server.WriteTimeout,
		CertContext:                server.CertContext,
		AllowNonGlobalDestinations: server.AllowNonGlobalDestinations,
		AllowedPorts:               server.AllowedPorts,
	}

	if server.cfg.Unencrypted {
		log.Debug("Running in unencrypted mode")
		fs.CertContext = nil
	}

	// Add callbacks to track bytes given
	fs.OnBytesReceived = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesReceived(ip, bytes)
	}
	fs.OnBytesSent = func(ip string, destAddr string, req *http.Request, bytes int64) {
		onBytesGiven(destAddr, req, bytes)
		statserver.OnBytesSent(ip, bytes)
	}

	l, err := fs.Listen()
	if err != nil {
		return fmt.Errorf("Unable to listen at %s: %s", server.Addr, err)
	}

	go server.register()

	return fs.Serve(l)
}

func (server *Server) register() {
	for {
		server.cfgMutex.RLock()
		baseUrl := server.cfg.RegisterAt
		server.cfgMutex.RUnlock()
		if baseUrl != "" {
			if globals.InstanceId == "" {
				log.Error("Unable to register server because no InstanceId is configured")
			} else {
				log.Debugf("Registering server at %v", baseUrl)
				registerUrl := baseUrl + "/register"
				vals := url.Values{
					"name": []string{globals.InstanceId},
					"port": []string{"443"},
				}
				resp, err := http.PostForm(registerUrl, vals)
				if err != nil {
					log.Errorf("Unable to register at %v: %v", registerUrl, err)
					return
				} else if resp.StatusCode != 200 {
					bodyString, _ := ioutil.ReadAll(resp.Body)
					log.Errorf("Unexpected response status registering at %v: %d    %v", registerUrl, resp.StatusCode, string(bodyString))
				} else {
					log.Debugf("Successfully registered server at %v", registerUrl)
				}
				resp.Body.Close()
				time.Sleep(registerPeriod)
			}
		}
	}
}

func (server *Server) startNattywad(waddellAddr string) {
	log.Debugf("Connecting to waddell at: %s", waddellAddr)
	var err error
	server.waddellClient, err = waddell.NewClient(&waddell.ClientConfig{
		Dial: func() (net.Conn, error) {
			return net.Dial("tcp", waddellAddr)
		},
		ServerCert:        globals.WaddellCert,
		ReconnectAttempts: 10,
		OnId: func(id waddell.PeerId) {
			log.Debugf("Connected to Waddell!! Id is: %s", id)
		},
	})
	if err != nil {
		log.Errorf("Unable to connect to waddell: %s", err)
		server.waddellClient = nil
		return
	}
	server.nattywadServer = &nattywad.Server{
		Client: server.waddellClient,
		OnSuccess: func(local *net.UDPAddr, remote *net.UDPAddr) bool {
			err := nattest.Serve(local)
			if err != nil {
				log.Error(err.Error())
				return false
			}
			return true
		},
	}
	server.nattywadServer.Start()
}

func (server *Server) stopNattywad() {
	log.Debug("Stopping nattywad server")
	server.nattywadServer.Stop()
	server.nattywadServer = nil
	log.Debug("Stopping waddell client")
	server.waddellClient.Close()
	server.waddellClient = nil
}

func mapPort(addr string, port int) error {
	internalIP, internalPortString, err := net.SplitHostPort(addr)
	if err != nil {
		return fmt.Errorf("Unable to split host and port for %v: %v", addr, err)
	}

	internalPort, err := strconv.Atoi(internalPortString)
	if err != nil {
		return fmt.Errorf("Unable to parse local port: ")
	}

	if internalIP == "" {
		internalIP, err = determineInternalIP()
		if err != nil {
			return fmt.Errorf("Unable to determine internal IP: %s", err)
		}
	}

	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	err = igd.AddPortMapping(igdman.TCP, internalIP, internalPort, port, 0)
	if err != nil {
		return fmt.Errorf("Unable to map port with igdman %d: %s", port, err)
	}

	return nil
}

func unmapPort(port int) error {
	igd, err := igdman.NewIGD()
	if err != nil {
		return fmt.Errorf("Unable to get IGD: %s", err)
	}

	igd.RemovePortMapping(igdman.TCP, port)
	if err != nil {
		return fmt.Errorf("Unable to unmap port with igdman %d: %s", port, err)
	}

	return nil
}

// determineInternalIP determines the internal IP to use for mapping ports. It
// does this by dialing a website on the public Internet and then finding out
// the LocalAddr for the corresponding connection. This gives us an interface
// that we know has Internet access, which makes it suitable for port mapping.
func determineInternalIP() (string, error) {
	conn, err := net.DialTimeout("tcp", "s3.amazonaws.com:443", 20*time.Second)
	if err != nil {
		return "", fmt.Errorf("Unable to determine local IP: %s", err)
	}
	defer conn.Close()
	host, _, err := net.SplitHostPort(conn.LocalAddr().String())
	return host, err
}

func onBytesGiven(destAddr string, req *http.Request, bytes int64) {
	host, port, _ := net.SplitHostPort(destAddr)
	if port == "" {
		port = "0"
	}

	given := statreporter.CountryDim().
		And("flserver", globals.InstanceId).
		And("destport", port)
	given.Increment("bytesGiven").Add(bytes)
	given.Increment("bytesGivenByFlashlight").Add(bytes)

	clientCountry := req.Header.Get("Cf-Ipcountry")
	if clientCountry != "" {
		givenTo := statreporter.Country(clientCountry)
		givenTo.Increment("bytesGivenTo").Add(bytes)
		givenTo.Increment("bytesGivenToByFlashlight").Add(bytes)
		givenTo.Member("distinctDestHosts", host)

		clientIp := req.Header.Get("X-Forwarded-For")
		if clientIp != "" {
			// clientIp may contain multiple ips, use the first
			ips := strings.Split(clientIp, ",")
			clientIp := strings.TrimSpace(ips[0])
			givenTo.Member("distinctClients", clientIp)
		}

	}
}

func hostFn(fqdns map[string]string) func(*http.Request) string {
	// We prefer to use the fronting provider through which we have been
	// reached, because we expect that to be unblocked, but if something goes
	// wrong (e.g. in old give mode peers) we'll use just any configured host.
	return func(req *http.Request) string {
		for provider, fn := range frontingProviders {
			if fn(req) {
				fqdn, found := fqdns[provider]
				if found {
					return fqdn
				}
			}
		}
		// We don't know about this provider or we don't have a fqdn for it...
		// The best we can do is provide *some* FQDN through which we hope we
		// can be reached.
		log.Debugf("Falling back to just any FQDN")
		for _, fqdn := range fqdns {
			return fqdn
		}
		// We don't know of any fqdns.  This will be treated like a null
		// hostFn.
		return ""
	}
}
<|endoftext|>


<commit_before>/*
 * siteshot is a website screenshot-capturing web application.
 * Copyright © 2016-2019 A Bunch Tell LLC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/fatih/color"
)

// configuration constants
const (
	port = 3333
)

var (
	infoLog *log.Logger
	errLog  *log.Logger
)

var (
	wd string

	// Thumbnail parameters
	thumbWidth  int
	thumbHeight int
)

func main() {
	red := color.New(color.FgRed).SprintFunc()
	blue := color.New(color.FgBlue).SprintFunc()
	errLog = log.New(os.Stderr, fmt.Sprintf("[%s] ", red("ERROR")), log.Ldate|log.Ltime|log.Lshortfile)
	infoLog = log.New(os.Stdout, fmt.Sprintf("[%s] ", blue("INFO")), log.Ldate|log.Ltime)

	// Get information about the environment
	var err error
	wd, err = os.Getwd()
	if err != nil {
		errLog.Printf("Couldn't get working dir: %v", err)
		return
	}

	// Set configuration
	dim := flag.String("dim", "", "Dimensions of final thumbnail")
	flag.Parse()
	if *dim != "" {
		c := strings.Split(*dim, "x")
		if len(c) != 2 {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
		thumbWidth, err = strconv.Atoi(c[0])
		if err != nil {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
		thumbHeight, err = strconv.Atoi(c[1])
		if err != nil {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
	} else {
		thumbWidth = 320
		thumbHeight = 240
	}

	// Start server
	infoLog.Printf("Listening on :%d", port)
	infoLog.Printf("Width: %d Height: %d", thumbWidth, thumbHeight)
	http.HandleFunc("/", makeThumbnail)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func makeThumbnail(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	url := r.FormValue("url")
	if url == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	infoLog.Printf("Received request for %s", url)

	thumbFile := strings.Replace(url[strings.Index(url, "://")+3:]+".png", "/", ".", -1)

	// Fetch the thumbnail
	thumb := exec.Command("xvfb-run", "--server-args", "-screen 0 1366x768x24", "webkit2png", "-o", thumbFile, url)
	if err := thumb.Run(); err != nil {
		errLog.Printf("xvfb-run: %v", err)
		return
	}

	// Resize to width
	cmd := exec.Command("convert", filepath.Join(wd, thumbFile), "-define", "png:big-depth=16", "-define", "png:color-type=6", "-thumbnail", fmt.Sprintf("%d", thumbWidth), filepath.Join(wd, thumbFile))
	if err := cmd.Run(); err != nil {
		errLog.Printf("convert -thumbnail: %v", err)
		return
	}

	// Crop to height
	cmd = exec.Command("convert", filepath.Join(wd, thumbFile), "-define", "png:big-depth=16", "-define", "png:color-type=6", "-crop", fmt.Sprintf("%dx%d+0+0", thumbWidth, thumbHeight), filepath.Join(wd, thumbFile))
	if err := cmd.Run(); err != nil {
		errLog.Printf("convert -crop: %v", err)
		return
	}

	infoLog.Printf(color.GreenString("✓") + " Created " + thumbFile)

	fmt.Fprintf(w, thumbFile)
}
<commit_msg>Support configuring port to run on<commit_after>/*
 * siteshot is a website screenshot-capturing web application.
 * Copyright © 2016-2019 A Bunch Tell LLC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/fatih/color"
)

var (
	infoLog *log.Logger
	errLog  *log.Logger
)

var (
	wd   string
	port = 3333

	// Thumbnail parameters
	thumbWidth  int
	thumbHeight int
)

func main() {
	red := color.New(color.FgRed).SprintFunc()
	blue := color.New(color.FgBlue).SprintFunc()
	errLog = log.New(os.Stderr, fmt.Sprintf("[%s] ", red("ERROR")), log.Ldate|log.Ltime|log.Lshortfile)
	infoLog = log.New(os.Stdout, fmt.Sprintf("[%s] ", blue("INFO")), log.Ldate|log.Ltime)

	// Get information about the environment
	var err error
	wd, err = os.Getwd()
	if err != nil {
		errLog.Printf("Couldn't get working dir: %v", err)
		return
	}

	// Set configuration
	dim := flag.String("dim", "", "Dimensions of final thumbnail")
	portPtr := flag.Int("p", 3333, "Port to serve on")
	flag.Parse()
	if *dim != "" {
		c := strings.Split(*dim, "x")
		if len(c) != 2 {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
		thumbWidth, err = strconv.Atoi(c[0])
		if err != nil {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
		thumbHeight, err = strconv.Atoi(c[1])
		if err != nil {
			errLog.Printf("usage: siteshot --dim 320x240")
			return
		}
	} else {
		thumbWidth = 320
		thumbHeight = 240
	}
	port = *portPtr

	// Start server
	infoLog.Printf("Listening on :%d", port)
	infoLog.Printf("Width: %d Height: %d", thumbWidth, thumbHeight)
	http.HandleFunc("/", makeThumbnail)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func makeThumbnail(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	url := r.FormValue("url")
	if url == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	infoLog.Printf("Received request for %s", url)

	thumbFile := strings.Replace(url[strings.Index(url, "://")+3:]+".png", "/", ".", -1)

	// Fetch the thumbnail
	thumb := exec.Command("xvfb-run", "--server-args", "-screen 0 1366x768x24", "webkit2png", "-o", thumbFile, url)
	if err := thumb.Run(); err != nil {
		errLog.Printf("xvfb-run: %v", err)
		return
	}

	// Resize to width
	cmd := exec.Command("convert", filepath.Join(wd, thumbFile), "-define", "png:big-depth=16", "-define", "png:color-type=6", "-thumbnail", fmt.Sprintf("%d", thumbWidth), filepath.Join(wd, thumbFile))
	if err := cmd.Run(); err != nil {
		errLog.Printf("convert -thumbnail: %v", err)
		return
	}

	// Crop to height
	cmd = exec.Command("convert", filepath.Join(wd, thumbFile), "-define", "png:big-depth=16", "-define", "png:color-type=6", "-crop", fmt.Sprintf("%dx%d+0+0", thumbWidth, thumbHeight), filepath.Join(wd, thumbFile))
	if err := cmd.Run(); err != nil {
		errLog.Printf("convert -crop: %v", err)
		return
	}

	infoLog.Printf(color.GreenString("✓") + " Created " + thumbFile)

	fmt.Fprintf(w, thumbFile)
}
<|endoftext|>