<commit_before>// Package mylogin reads ~/.mylogin.cnf created by mysql_config_editor
//
// See https://dev.mysql.com/doc/refman/5.7/en/mysql-config-editor.html
//
// Example:
//   mysql_config_editor set --login-path=foo --user=bar -p
package mylogin

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"encoding/binary"
	"fmt"
	"io"
	"os"
)

// DefaultSection is the name of the base section used by all MySQL client tools.
const DefaultSection = "client"

// KeySize is the size in bytes of the key used for encryption of mylogin.cnf
// files.
const KeySize = 20

// DefaultFile returns the path to the default mylogin.cnf file:
//   Windows: %APPDATA%/MySQL/.mylogin.cnf
//   others: ~/.mylogin.cnf
// If the environment variable MYSQL_TEST_LOGIN_FILE is set
// that path is returned instead.
func DefaultFile() string {
	f := os.Getenv("MYSQL_TEST_LOGIN_FILE")
	if len(f) != 0 {
		return f
	}
	// see defaultfile.go, defaultfile_windows.go
	return platformDefaultFile()
}

// ReadLogin reads a mylogin.cnf file, extracts the requested sections and
// merges them to obtain a single Login (that may be empty).
func ReadLogin(filename string, sectionNames []string) (login *Login, err error) {
	sections, err := ReadSections(filename)
	if err != nil {
		return
	}
	login = sections.Merge(sectionNames)
	return
}

// ReadSections reads all Sections of a mylogin.cnf file.
func ReadSections(filename string) (sections Sections, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	file, err := Decode(bufio.NewReader(f))
	if err != nil {
		return
	}
	return Parse(file.PlainText())
}

// Parse parses the plaintext content of a mylogin.cnf file
// and returns the structured content.
func Parse(rd io.Reader) (sections Sections, err error) {
	var login *Login
	scanner := bufio.NewScanner(rd)
	for scanner.Scan() {
		line := scanner.Text()
		if line[0] == '[' {
			sections = append(sections,
				Section{Name: line[1 : len(line)-1]})
			login = &sections[len(sections)-1].Login
		} else if login != nil && line != "" {
			if err = login.parseLine(line); err != nil {
				return nil, err
			}
		}
	}
	return
}

// File is the full structure of a mylogin.cnf file.
type File interface {
	// The key used for encrypting the file
	Key() [KeySize]byte
	// Byte ordering for saving int32 chunk sizes
	ByteOrder() binary.ByteOrder
	// The plaintext content of the file
	PlainText() io.Reader
}

type decoder struct {
	key       [KeySize]byte
	byteOrder binary.ByteOrder

	input  io.Reader
	block  cipher.Block
	chunk  [256 * aes.BlockSize]byte
	buffer []byte // Slice pointing to chunk
}

func (d *decoder) Key() [KeySize]byte {
	return d.key
}

func (d *decoder) ByteOrder() binary.ByteOrder {
	return d.byteOrder
}

func (d *decoder) PlainText() io.Reader {
	return d
}

func (d *decoder) Parse() (Sections, error) {
	return Parse(d)
}

// Decode is a filter that returns the plaintext content of a mylogin.cnf file.
// The file is encrypted with AES 128 CBC with the key embeded in the file.
func Decode(input io.Reader) (File, error) {
	// http://ocelot.ca/blog/blog/2015/05/21/decrypt-mylogin-cnf/

	in := bufio.NewReader(input)

	// Skip first 4 bytes
	head4 := make([]byte, 4)
	n, err := in.Read(head4)
	if err != nil {
		return nil, err
	}
	if n != 4 {
		return nil, io.EOF
	}

	var key [KeySize]byte
	n, err = in.Read(key[:])
	if err != nil {
		return nil, err
	}
	if n != len(key) {
		return nil, io.EOF
	}
	// log.Printf("Key: %v\n", key)

	// The following 4 bytes are the length of the first chunk
	// We will use them to detect the byte order
	chunkSize, err := in.Peek(4)
	if err != nil {
		return nil, err
	}
	var byteOrder binary.ByteOrder
	// Assume all chunks have size < 64K
	if chunkSize[0] == 0 && chunkSize[1] == 0 && (chunkSize[2] != 0 || chunkSize[3] != 0) {
		byteOrder = binary.BigEndian
	} else {
		byteOrder = binary.LittleEndian
	}

	// 16 bytes key for AES-128
	var aesKey [16]byte
	// Apply xor
	for i := 0; i < KeySize; i++ {
		aesKey[i%16] ^= key[i]
	}

	block, err := aes.NewCipher(aesKey[:])
	if err != nil {
		panic(err.Error())
	}

	return &decoder{key: key, input: in, byteOrder: byteOrder, block: block}, nil
}

// Read is the PlainText reader.
func (d *decoder) Read(buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return
	}
	if len(d.buffer) > 0 {
		n = copy(buf, d.buffer)
		d.buffer = d.buffer[n:]
		return
	}
	var size int32
	for {
		// Read a new chunk
		err = binary.Read(d.input, d.byteOrder, &size)
		if err != nil {
			return 0, err
		}
		if size != 0 {
			break
		}
	}
	if size < 0 || int(size) > len(d.chunk) || size%aes.BlockSize != 0 {
		return 0, fmt.Errorf("invalid block size: %d", size)
	}
	n, err = d.input.Read(d.chunk[:size])
	if n != int(size) {
		if err == nil {
			err = io.EOF
		}
		return 0, err
	}

	// Each 16-bytes block is encoded with a null IV
	d.buffer = d.chunk[:size]
	for i := 0; i < int(size); i += aes.BlockSize {
		cbc := cipher.NewCBCDecrypter(d.block, make([]byte, aes.BlockSize))
		b := d.chunk[i : i+aes.BlockSize]
		cbc.CryptBlocks(b, b)

	}

	// Remove PKCS#7 padding
	// last byte value gives the number of padding byte
	// each padding byte has that value
	padding := d.buffer[len(d.buffer)-1]
	// Note that mysql_config_editor generates up to 16 bytes of padding
	// which is a full AES block, so 16 encrypted bytes just to be drop when
	// reading.
	// Is it a bug or some nasty redundancy to reveal the encryption key?
	if padding > 0 && padding <= aes.BlockSize {
		//log.Printf("Padding: %d\n", padding)
		for _, c := range d.buffer[len(d.buffer)-int(padding):] {
			if c != padding {
				padding = 0
				break
			}
		}
		d.buffer = d.buffer[:len(d.buffer)-int(padding)]
	}

	n = copy(buf, d.buffer)
	d.buffer = d.buffer[n:]
	return
}

/*
// TODO

func Encode(io.Writer, f File) error {
}
*/
<commit_msg>Replace const KeySize with type Key<commit_after>// Package mylogin reads ~/.mylogin.cnf created by mysql_config_editor
//
// See https://dev.mysql.com/doc/refman/5.7/en/mysql-config-editor.html
//
// Example:
//   mysql_config_editor set --login-path=foo --user=bar -p
package mylogin

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"encoding/binary"
	"fmt"
	"io"
	"os"
)

// DefaultSection is the name of the base section used by all MySQL client tools.
const DefaultSection = "client"

// Key is a key used for encryption of mylogin.cnf files.
type Key [20]byte

// DefaultFile returns the path to the default mylogin.cnf file:
//   Windows: %APPDATA%/MySQL/.mylogin.cnf
//   others: ~/.mylogin.cnf
// If the environment variable MYSQL_TEST_LOGIN_FILE is set
// that path is returned instead.
func DefaultFile() string {
	f := os.Getenv("MYSQL_TEST_LOGIN_FILE")
	if len(f) != 0 {
		return f
	}
	// see defaultfile.go, defaultfile_windows.go
	return platformDefaultFile()
}

// ReadLogin reads a mylogin.cnf file, extracts the requested sections and
// merges them to obtain a single Login (that may be empty).
func ReadLogin(filename string, sectionNames []string) (login *Login, err error) {
	sections, err := ReadSections(filename)
	if err != nil {
		return
	}
	login = sections.Merge(sectionNames)
	return
}

// ReadSections reads all Sections of a mylogin.cnf file.
func ReadSections(filename string) (sections Sections, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer f.Close()

	file, err := Decode(bufio.NewReader(f))
	if err != nil {
		return
	}
	return Parse(file.PlainText())
}

// Parse parses the plaintext content of a mylogin.cnf file
// and returns the structured content.
func Parse(rd io.Reader) (sections Sections, err error) {
	var login *Login
	scanner := bufio.NewScanner(rd)
	for scanner.Scan() {
		line := scanner.Text()
		if line[0] == '[' {
			sections = append(sections,
				Section{Name: line[1 : len(line)-1]})
			login = &sections[len(sections)-1].Login
		} else if login != nil && line != "" {
			if err = login.parseLine(line); err != nil {
				return nil, err
			}
		}
	}
	return
}

// File is the full structure of a mylogin.cnf file.
type File interface {
	// The key used for encrypting the file
	Key() Key
	// Byte ordering for saving int32 chunk sizes
	ByteOrder() binary.ByteOrder
	// The plaintext content of the file
	PlainText() io.Reader
}

type decoder struct {
	key       Key
	byteOrder binary.ByteOrder

	input  io.Reader
	block  cipher.Block
	chunk  [256 * aes.BlockSize]byte
	buffer []byte // Slice pointing to chunk
}

func (d *decoder) Key() Key {
	return d.key
}

func (d *decoder) ByteOrder() binary.ByteOrder {
	return d.byteOrder
}

func (d *decoder) PlainText() io.Reader {
	return d
}

func (d *decoder) Parse() (Sections, error) {
	return Parse(d)
}

// Decode is a filter that returns the plaintext content of a mylogin.cnf file.
// The file is encrypted with AES 128 CBC with the key embeded in the file.
func Decode(input io.Reader) (File, error) {
	// http://ocelot.ca/blog/blog/2015/05/21/decrypt-mylogin-cnf/

	in := bufio.NewReader(input)

	// Skip first 4 bytes
	head4 := make([]byte, 4)
	n, err := in.Read(head4)
	if err != nil {
		return nil, err
	}
	if n != 4 {
		return nil, io.EOF
	}

	var key Key
	n, err = in.Read(key[:])
	if err != nil {
		return nil, err
	}
	if n != cap(key) {
		return nil, io.EOF
	}
	// log.Printf("Key: %v\n", key)

	// The following 4 bytes are the length of the first chunk
	// We will use them to detect the byte order
	chunkSize, err := in.Peek(4)
	if err != nil {
		return nil, err
	}
	var byteOrder binary.ByteOrder
	// Assume all chunks have size < 64K
	if chunkSize[0] == 0 && chunkSize[1] == 0 && (chunkSize[2] != 0 || chunkSize[3] != 0) {
		byteOrder = binary.BigEndian
	} else {
		byteOrder = binary.LittleEndian
	}

	// 16 bytes key for AES-128
	var aesKey [16]byte
	// Apply xor
	for i := range key {
		aesKey[i%16] ^= key[i]
	}

	block, err := aes.NewCipher(aesKey[:])
	if err != nil {
		panic(err.Error())
	}

	return &decoder{key: key, input: in, byteOrder: byteOrder, block: block}, nil
}

// Read is the PlainText reader.
func (d *decoder) Read(buf []byte) (n int, err error) {
	if len(buf) == 0 {
		return
	}
	if len(d.buffer) > 0 {
		n = copy(buf, d.buffer)
		d.buffer = d.buffer[n:]
		return
	}
	var size int32
	for {
		// Read a new chunk
		err = binary.Read(d.input, d.byteOrder, &size)
		if err != nil {
			return 0, err
		}
		if size != 0 {
			break
		}
	}
	if size < 0 || int(size) > len(d.chunk) || size%aes.BlockSize != 0 {
		return 0, fmt.Errorf("invalid block size: %d", size)
	}
	n, err = d.input.Read(d.chunk[:size])
	if n != int(size) {
		if err == nil {
			err = io.EOF
		}
		return 0, err
	}

	// Each 16-bytes block is encoded with a null IV
	d.buffer = d.chunk[:size]
	for i := 0; i < int(size); i += aes.BlockSize {
		cbc := cipher.NewCBCDecrypter(d.block, make([]byte, aes.BlockSize))
		b := d.chunk[i : i+aes.BlockSize]
		cbc.CryptBlocks(b, b)

	}

	// Remove PKCS#7 padding
	// last byte value gives the number of padding byte
	// each padding byte has that value
	padding := d.buffer[len(d.buffer)-1]
	// Note that mysql_config_editor generates up to 16 bytes of padding
	// which is a full AES block, so 16 encrypted bytes just to be drop when
	// reading.
	// Is it a bug or some nasty redundancy to reveal the encryption key?
	if padding > 0 && padding <= aes.BlockSize {
		//log.Printf("Padding: %d\n", padding)
		for _, c := range d.buffer[len(d.buffer)-int(padding):] {
			if c != padding {
				padding = 0
				break
			}
		}
		d.buffer = d.buffer[:len(d.buffer)-int(padding)]
	}

	n = copy(buf, d.buffer)
	d.buffer = d.buffer[n:]
	return
}

/*
// TODO

func Encode(io.Writer, f File) error {
}
*/
<|endoftext|>


<commit_before>package app_budget

import (
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/essentials/log/esl_rotate"
	"math"
)

const (
	unlimitedChunkSize = math.MaxInt64 // no rotate
	unlimitedQuota     = esl_rotate.UnlimitedQuota
	unlimitedNumBackup = esl_rotate.UnlimitedBackups
	standardChunkSize  = 200 * 1024    // 200KiB
	standardQuota      = 500 * 1048576 // 500MiB * 2 = 1000MiB
	standardNumBackup  = esl_rotate.UnlimitedBackups
	lowChunkSize       = 100 * 1024    // 100KiB
	lowQuota           = 50 * 1048576  // 5MiB * 2 =  100MiB
	lowNumBackup       = esl_rotate.UnlimitedBackups

	BudgetLow       Budget = "low"
	BudgetNormal    Budget = "normal"
	BudgetUnlimited Budget = "unlimited"
)

type Budget string

var (
	StorageBudgets = []string{
		string(BudgetLow), string(BudgetNormal), string(BudgetUnlimited),
	}
	DefaultBudget = BudgetNormal
)

func StorageBudget(budget Budget) (chunkSize, quota int64, numBackup int) {
	switch budget {
	case BudgetLow:
		return lowChunkSize, lowQuota, lowNumBackup
	case BudgetNormal:
		return standardChunkSize, standardQuota, standardNumBackup
	case BudgetUnlimited:
		return unlimitedChunkSize, unlimitedQuota, unlimitedNumBackup
	default:
		l := esl.Default()
		l.Error("Unsupported budget type, fallback to BudgetNormal", esl.String("budget", string(budget)))
		return StorageBudget(BudgetNormal)
	}
}
<commit_msg>fix #595 : update log quota<commit_after>package app_budget

import (
	"github.com/watermint/toolbox/essentials/log/esl"
	"github.com/watermint/toolbox/essentials/log/esl_rotate"
	"math"
)

const (
	unlimitedChunkSize = math.MaxInt64 // no rotate
	unlimitedQuota     = esl_rotate.UnlimitedQuota
	unlimitedNumBackup = esl_rotate.UnlimitedBackups
	standardChunkSize  = 100 * 1048576  // 100MiB
	standardQuota      = 5 * 1073741824 // 5GiB * 2 = 10GiB
	standardNumBackup  = standardQuota * 2 / standardChunkSize
	lowChunkSize       = 10 * 1048576  // 10MiB
	lowQuota           = 500 * 1048576 // 500MiB * 2 = 1GiB
	lowNumBackup       = lowQuota * 2 / lowChunkSize

	BudgetLow       Budget = "low"
	BudgetNormal    Budget = "normal"
	BudgetUnlimited Budget = "unlimited"
)

type Budget string

var (
	StorageBudgets = []string{
		string(BudgetLow), string(BudgetNormal), string(BudgetUnlimited),
	}
	DefaultBudget = BudgetNormal
)

func StorageBudget(budget Budget) (chunkSize, quota int64, numBackup int) {
	switch budget {
	case BudgetLow:
		return lowChunkSize, lowQuota, lowNumBackup
	case BudgetNormal:
		return standardChunkSize, standardQuota, standardNumBackup
	case BudgetUnlimited:
		return unlimitedChunkSize, unlimitedQuota, unlimitedNumBackup
	default:
		l := esl.Default()
		l.Error("Unsupported budget type, fallback to BudgetNormal", esl.String("budget", string(budget)))
		return StorageBudget(BudgetNormal)
	}
}
<|endoftext|>


<commit_before>package wundergo_integration_test

import (
	"fmt"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

func runMainWithArgs(args ...string) *gexec.Session {
	command := exec.Command(wlBinPath, args...)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).NotTo(HaveOccurred())
	return session
}

var _ = Describe("wl binary", func() {
	var (
		args []string
	)

	BeforeEach(func() {
		args = []string{}
	})

	Describe("Displaying version", func() {
		It("displays version with 'version'", func() {
			args = append(args, fmt.Sprintf("version"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("displays version with '-v'", func() {
			args = append(args, fmt.Sprintf("-v"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("displays version with '--version'", func() {
			args = append(args, fmt.Sprintf("--version"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})
	})

	Context("when provided with valid credentials", func() {
		BeforeEach(func() {
			args = append(args, fmt.Sprintf("-accessToken=%s", wlAccessToken))
			args = append(args, fmt.Sprintf("-clientID=%s", wlClientID))
		})

		It("exits with failure code if no arguments are provided", func() {
			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gexec.Exit(2))
		})

		Describe("list operations", func() {
			It("gets all lists", func() {
				args = append(args, fmt.Sprintf("lists"))

				command := exec.Command(wlBinPath, args...)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Eventually(session).Should(gbytes.Say(`"list_type":"inbox"`))
				Eventually(session).Should(gexec.Exit(0))
			})
		})

		Describe("folder operations", func() {
			It("gets all folders", func() {
				args = append(args, fmt.Sprintf("folders"))

				command := exec.Command(wlBinPath, args...)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				// We have no idea how many folders we should expect
				// it could be many, or an empty list
				// Either way we check for '[' and ']' which will always be rendered
				// These characters are escaped to prevent them being interpreted as regex
				Eventually(session).Should(gbytes.Say("\\["))
				Eventually(session).Should(gbytes.Say("\\]"))
				Eventually(session).Should(gexec.Exit(0))
			})
		})

		Describe("task operations", func() {
			It("gets all tasks", func() {
				args = append(args, fmt.Sprintf("tasks"))

				command := exec.Command(wlBinPath, args...)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				// We have no idea how many tasks we should expect
				// it could be many, or an empty list
				// Either way we check for '[' and ']' which will always be rendered
				// These characters are escaped to prevent them being interpreted as regex
				Eventually(session).Should(gbytes.Say("\\["))
				Eventually(session).Should(gbytes.Say("\\]"))
				Eventually(session).Should(gexec.Exit(0))
			})
		})
	})
})
<commit_msg>Remove wl binary task test.<commit_after>package wundergo_integration_test

import (
	"fmt"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

func runMainWithArgs(args ...string) *gexec.Session {
	command := exec.Command(wlBinPath, args...)
	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).NotTo(HaveOccurred())
	return session
}

var _ = Describe("wl binary", func() {
	var (
		args []string
	)

	BeforeEach(func() {
		args = []string{}
	})

	Describe("Displaying version", func() {
		It("displays version with 'version'", func() {
			args = append(args, fmt.Sprintf("version"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("displays version with '-v'", func() {
			args = append(args, fmt.Sprintf("-v"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})

		It("displays version with '--version'", func() {
			args = append(args, fmt.Sprintf("--version"))

			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gbytes.Say("dev"))
			Eventually(session).Should(gexec.Exit(0))
		})
	})

	Context("when provided with valid credentials", func() {
		BeforeEach(func() {
			args = append(args, fmt.Sprintf("-accessToken=%s", wlAccessToken))
			args = append(args, fmt.Sprintf("-clientID=%s", wlClientID))
		})

		It("exits with failure code if no arguments are provided", func() {
			command := exec.Command(wlBinPath, args...)
			session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			Eventually(session).Should(gexec.Exit(2))
		})

		Describe("list operations", func() {
			It("gets all lists", func() {
				args = append(args, fmt.Sprintf("lists"))

				command := exec.Command(wlBinPath, args...)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Eventually(session).Should(gbytes.Say(`"list_type":"inbox"`))
				Eventually(session).Should(gexec.Exit(0))
			})
		})

		Describe("folder operations", func() {
			It("gets all folders", func() {
				args = append(args, fmt.Sprintf("folders"))

				command := exec.Command(wlBinPath, args...)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				// We have no idea how many folders we should expect
				// it could be many, or an empty list
				// Either way we check for '[' and ']' which will always be rendered
				// These characters are escaped to prevent them being interpreted as regex
				Eventually(session).Should(gbytes.Say("\\["))
				Eventually(session).Should(gbytes.Say("\\]"))
				Eventually(session).Should(gexec.Exit(0))
			})
		})
	})
})
<|endoftext|>


<commit_before>package osversion

const (

	// RS2 was a client-only release in case you're asking why it's not in the list.
	RS1 = 14393
	RS3 = 16299
	RS4 = 17134
	RS5 = 17724 // TODO Bump to final RS5 build

)
<commit_msg>Bump RS5 build number<commit_after>package osversion

const (

	// RS2 was a client-only release in case you're asking why it's not in the list.
	RS1 = 14393
	RS3 = 16299
	RS4 = 17134
	RS5 = 17743 // TODO Bump to final RS5 build

)
<|endoftext|>


<commit_before>package dsalg

// Reference: sort.IntSlice.Less
func IntLess(a, b interface{}) bool {
	return a.(int) < b.(int)
}

// Reference: A copy of sort.Float64Slice.Less
func Float64Less(a, b interface{}) bool {
	return a.(float64) < b.(float64) || isNaN(a.(float64)) && !isNaN(b.(float64))
}

// Reference: A copy of sort.StringSlice.Less
func StringLess(a, b interface{}) bool {
	return a.(string) < b.(string)
}

type BinarySearchTreeNode struct {
	leftChild, rightChild *BinarySearchTreeNode
	value                 interface{}
	count                 int
}

// Returns whether a new node is allocated
func (node *BinarySearchTreeNode) insert(ptrFromParent **BinarySearchTreeNode, less func(interface{}, interface{}) bool, v interface{}) bool {
	if node == nil {
		*ptrFromParent = &BinarySearchTreeNode{value: v, count: 1}
		return true
	}
	if less(v, node.value) {
		return node.leftChild.insert(&node.leftChild, less, v)
	} else if less(node.value, v) {
		return node.rightChild.insert(&node.rightChild, less, v)
	} else {
		node.count++
		return false
	}
}

func (node *BinarySearchTreeNode) TraversePreOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	for i := 0; i < node.count; i++ {
		consumer(node.value)
	}
	node.leftChild.TraversePreOrder(consumer)
	node.rightChild.TraversePreOrder(consumer)
}

func (node *BinarySearchTreeNode) TraverseInOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	node.leftChild.TraverseInOrder(consumer)
	for i := 0; i < node.count; i++ {
		consumer(node.value)
	}
	node.rightChild.TraverseInOrder(consumer)
}

func (node *BinarySearchTreeNode) TraversePostOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	node.leftChild.TraversePostOrder(consumer)
	node.rightChild.TraversePostOrder(consumer)
	for i := 0; i < node.count; i++ {
		consumer(node.value)
	}
}

type BinarySearchTree struct {
	less func(interface{}, interface{}) bool
	root *BinarySearchTreeNode
	len  int
	cap  int
}

func NewBinarySearchTree(less func(interface{}, interface{}) bool) *BinarySearchTree {
	return &BinarySearchTree{less: less}
}

func NewIntBinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: IntLess}
}

func NewFloat64BinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: Float64Less}
}

func NewStringBinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: StringLess}
}

func (tree *BinarySearchTree) Insert(v interface{}) {
	if tree.root.insert(&tree.root, tree.less, v) {
		tree.cap++
	}
	tree.len++
}

func (tree *BinarySearchTree) TraversePreOrder(consumer func(interface{})) {
	tree.root.TraversePreOrder(consumer)
}

func (tree *BinarySearchTree) TraverseInOrder(consumer func(interface{})) {
	tree.root.TraverseInOrder(consumer)
}

func (tree *BinarySearchTree) TraversePostOrder(consumer func(interface{})) {
	tree.root.TraversePostOrder(consumer)
}

func (tree *BinarySearchTree) Len() int {
	return tree.len
}

func (tree *BinarySearchTree) Cap() int {
	return tree.cap
}

func (tree *BinarySearchTree) IsEmpty() bool {
	return tree.len == 0
}

func (tree *BinarySearchTree) IsFull() bool {
	return false
}
<commit_msg>Big updates<commit_after>package dsalg

// Reference: sort.IntSlice.Less
func IntLess(a, b interface{}) bool {
	return a.(int) < b.(int)
}

// Reference: sort.Float64Slice.Less
func Float64Less(a, b interface{}) bool {
	return a.(float64) < b.(float64) || isNaN(a.(float64)) && !isNaN(b.(float64))
}

// Reference: sort.StringSlice.Less
func StringLess(a, b interface{}) bool {
	return a.(string) < b.(string)
}

type BinarySearchTreeNode struct {
	// Left child and right child pointers in the binary search tree.
	leftChild, rightChild *BinarySearchTreeNode

	// The value stored with this node.
	value interface{}
}

// LeftChild returns the left child of this node or nil.
func (node *BinarySearchTreeNode) LeftChild() *BinarySearchTreeNode {
	return node.leftChild
}

// RightChild returns the right child of this node or nil.
func (node *BinarySearchTreeNode) RightChild() *BinarySearchTreeNode {
	return node.rightChild
}

// Value returns the value stored with this node.
func (node *BinarySearchTreeNode) Value() interface{} {
	return node.value
}

// Returns whether a new node is allocated
func (node *BinarySearchTreeNode) insert(v interface{}, less func(interface{}, interface{}) bool) (*BinarySearchTreeNode, *BinarySearchTreeNode, bool) {
	if node == nil {
		newNode := &BinarySearchTreeNode{value: v}
		return newNode, newNode, true
	}
	if less(v, node.value) {
		newRoot, targetNode, isCreated := node.leftChild.insert(v, less)
		node.leftChild = newRoot
		return node, targetNode, isCreated
	} else if less(node.value, v) {
		newRoot, targetNode, isCreated := node.rightChild.insert(v, less)
		node.rightChild = newRoot
		return node, targetNode, isCreated
	} else {
		return node, node, false
	}
}

func (node *BinarySearchTreeNode) TraversePreOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	consumer(node.value)
	node.leftChild.TraversePreOrder(consumer)
	node.rightChild.TraversePreOrder(consumer)
}

func (node *BinarySearchTreeNode) TraverseInOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	node.leftChild.TraverseInOrder(consumer)
	consumer(node.value)
	node.rightChild.TraverseInOrder(consumer)
}

func (node *BinarySearchTreeNode) TraversePostOrder(consumer func(interface{})) {
	if node == nil {
		return
	}
	node.leftChild.TraversePostOrder(consumer)
	node.rightChild.TraversePostOrder(consumer)
	consumer(node.value)
}

type BinarySearchTree struct {
	less func(interface{}, interface{}) bool
	root *BinarySearchTreeNode
	len  int
	cap  int
}

func NewBinarySearchTree(less func(interface{}, interface{}) bool) *BinarySearchTree {
	return &BinarySearchTree{less: less}
}

func NewIntBinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: IntLess}
}

func NewFloat64BinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: Float64Less}
}

func NewStringBinarySearchTree() *BinarySearchTree {
	return &BinarySearchTree{less: StringLess}
}

func (tree *BinarySearchTree) Insert(v interface{}) *BinarySearchTreeNode {
	newRoot, targetNode, isCreated := tree.root.insert(v, tree.less)
	tree.root = newRoot
	tree.len++
	if isCreated {
		tree.cap++
	}
	return targetNode
}

func (tree *BinarySearchTree) TraversePreOrder(consumer func(interface{})) {
	tree.root.TraversePreOrder(consumer)
}

func (tree *BinarySearchTree) TraverseInOrder(consumer func(interface{})) {
	tree.root.TraverseInOrder(consumer)
}

func (tree *BinarySearchTree) TraversePostOrder(consumer func(interface{})) {
	tree.root.TraversePostOrder(consumer)
}

func (tree *BinarySearchTree) Len() int {
	return tree.len
}

func (tree *BinarySearchTree) Cap() int {
	return tree.cap
}

func (tree *BinarySearchTree) IsEmpty() bool {
	return tree.len == 0
}

func (tree *BinarySearchTree) IsFull() bool {
	return false
}
<|endoftext|>


<commit_before>// +build ignore

// Confusables table generator.
// See http://www.unicode.org/reports/tr39/

package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func main() {
	flag.Parse()
	loadUnicodeData()
	makeTables()
}

var url = flag.String("url",
	"http://www.unicode.org/Public/security/latest/",
	"URL of Unicode database directory")

var localFiles = flag.Bool("local",
	false,
	"data files have been copied to the current directory; for debugging only")

// confusables.txt has form:
//	309C ;	030A ;	SL	#* ( ゜ → ̊ ) KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK → COMBINING RING ABOVE	# →ﾟ→→゚→
// See http://www.unicode.org/reports/tr39/ for full explanation
// The fields:
const (
	CSourceCodePoint = iota
	CTargetCodePoint
	CType
	NumField

	MaxChar = 0x10FFFF // anything above this shouldn't exist
)

func openReader(file string) (input io.ReadCloser) {
	if *localFiles {
		f, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		input = f
	} else {
		path := *url + file
		log.Println("Downloading " + path)
		resp, err := http.Get(path)
		if err != nil {
			log.Fatal(err)
		}
		if resp.StatusCode != 200 {
			log.Fatal("bad GET status for "+path, resp.Status)
		}
		input = resp.Body
	}
	return
}

func parsePoint(pointString string, line string) rune {
	x, err := strconv.ParseUint(pointString, 16, 64)
	point := rune(x)
	if err != nil {
		log.Fatalf("%.5s...: %s", line, err)
	}
	if point == 0 {
		log.Fatalf("%5s: Unknown rune %X", line, point)
	}
	if point > MaxChar {
		log.Fatalf("%5s: Rune %X > MaxChar (%X)", line, point, MaxChar)
	}

	return point
}

// Type C encapsulates a line of the confusables.txt files
type C struct {
	k rune
	v []rune
}

var confusables []C

func parseCharacter(line string) {
	if len(line) == 0 || line[0] == '#' {
		return
	}
	field := strings.Split(line, " ;\t")
	if len(field) != NumField {
		log.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField)
	}

	if !strings.HasPrefix(field[2], "MA") {
		// The MA table is a superset anyway
		return
	}

	sourceRune := parsePoint(field[CSourceCodePoint], line)
	var targetRune []rune
	targetCodePoints := strings.Split(field[CTargetCodePoint], " ")
	for _, targetCP := range targetCodePoints {
		targetRune = append(targetRune, parsePoint(targetCP, line))
	}

	confusables = append(confusables, C{sourceRune, targetRune})
}

var originalHeader = []byte(`
// Following is the original header of the source confusables.txt file
//
`)

func parseHeader(line string) bool {
	// strip BOM
	if len(line) > 3 && bytes.Compare(([]byte(line[0:3])), []byte{0xEF, 0xBB, 0xBF}) == 0 {
		line = line[3:]
	}
	if len(line) == 0 || line[0] != '#' {
		return true
	}
	originalHeader = append(originalHeader, "//"+line[1:]+"\n"...)
	return false
}

func loadUnicodeData() {
	f := openReader("confusables.txt")
	defer f.Close()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		if parseHeader(scanner.Text()) {
			break
		}
	}
	for scanner.Scan() {
		parseCharacter(scanner.Text())
	}
	if scanner.Err() != nil {
		log.Fatal(scanner.Err())
	}
}

func makeTables() {
	out := fmt.Sprintf("%s\n", originalHeader)
	out += fmt.Sprint("var confusablesMap = map[rune]string{\n\n")
	for _, c := range confusables {
		out += fmt.Sprintf("0x%.8X: %+q,\n", c.k, string(c.v))
	}
	out += fmt.Sprintln("}")

	WriteGoFile("tables.go", "confusables", []byte(out))
}

const header = `// This file was generated by go generate; DO NOT EDIT

package %s

`

func WriteGoFile(filename, pkg string, b []byte) {
	w, err := os.Create(filename)
	if err != nil {
		log.Fatalf("Could not create file %s: %v", filename, err)
	}
	defer w.Close()
	_, err = fmt.Fprintf(w, header, pkg)
	if err != nil {
		log.Fatalf("Error writing header: %v", err)
	}
	// Strip leading newlines.
	for len(b) > 0 && b[0] == '\n' {
		b = b[1:]
	}
	formatted, err := format.Source(b)
	if err != nil {
		// Print the original buffer even in case of an error so that the
		// returned error can be meaningfully interpreted.
		w.Write(b)
		log.Fatalf("Error formatting file %s: %v", filename, err)
	}
	if _, err := w.Write(formatted); err != nil {
		log.Fatalf("Error writing file %s: %v", filename, err)
	}
}
<commit_msg>fixes to maketables<commit_after>// +build ignore

// Confusables table generator.
// See http://www.unicode.org/reports/tr39/

package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func main() {
	flag.Parse()
	loadUnicodeData()
	makeTables()
}

var url = flag.String("url",
	"https://www.unicode.org/Public/security/latest/",
	"URL of Unicode database directory")

var localFiles = flag.Bool("local",
	false,
	"data files have been copied to the current directory; for debugging only")

// confusables.txt has form:
//	309C ;	030A ;	SL	#* ( ゜ → ̊ ) KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK → COMBINING RING ABOVE	# →ﾟ→→゚→
// See http://www.unicode.org/reports/tr39/ for full explanation
// The fields:
const (
	CSourceCodePoint = iota
	CTargetCodePoint
	CType
	NumField

	MaxChar = 0x10FFFF // anything above this shouldn't exist
)

func openReader(file string) (input io.ReadCloser) {
	if *localFiles {
		f, err := os.Open(file)
		if err != nil {
			log.Fatal(err)
		}
		input = f
	} else {
		path := *url + file
		log.Println("Downloading " + path)
		resp, err := http.Get(path)
		if err != nil {
			log.Fatal(err)
		}
		if resp.StatusCode != 200 {
			log.Fatal("bad GET status for "+path, resp.Status)
		}
		input = resp.Body
	}
	return
}

func parsePoint(pointString string, line string) rune {
	x, err := strconv.ParseUint(pointString, 16, 64)
	point := rune(x)
	if err != nil {
		log.Fatalf("%.5s...: %s", line, err)
	}
	if point == 0 {
		log.Fatalf("%5s: Unknown rune %X", line, point)
	}
	if point > MaxChar {
		log.Fatalf("%5s: Rune %X > MaxChar (%X)", line, point, MaxChar)
	}

	return point
}

// Type C encapsulates a line of the confusables.txt files
type C struct {
	k rune
	v []rune
}

var confusables []C

func parseCharacter(line string) {
	if len(line) == 0 || line[0] == '#' {
		return
	}
	field := strings.Split(line, " ;\t")
	if len(field) != NumField {
		log.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField)
	}

	if !strings.HasPrefix(field[2], "MA") {
		// The MA table is a superset anyway
		return
	}

	sourceRune := parsePoint(field[CSourceCodePoint], line)
	var targetRune []rune
	targetCodePoints := strings.Split(field[CTargetCodePoint], " ")
	for _, targetCP := range targetCodePoints {
		targetRune = append(targetRune, parsePoint(targetCP, line))
	}

	confusables = append(confusables, C{sourceRune, targetRune})
}

var originalHeader = []byte(`
// Following is the original header of the source confusables.txt file
//
`)

func parseHeader(line string) bool {
	// strip BOM
	if len(line) > 3 && bytes.Compare(([]byte(line[0:3])), []byte{0xEF, 0xBB, 0xBF}) == 0 {
		line = line[3:]
	}
	if len(line) == 0 || line[0] != '#' {
		return true
	}
	originalHeader = append(originalHeader, "//"+line[1:]+"\n"...)
	return false
}

func loadUnicodeData() {
	f := openReader("confusables.txt")
	defer f.Close()
	scanner := bufio.NewScanner(f)
	inHeader := true
	for scanner.Scan() {
		line := scanner.Text()
		if inHeader {
			inHeader = !parseHeader(line)
		}
		if !inHeader {
			parseCharacter(line)
		}
	}
	if scanner.Err() != nil {
		log.Fatal(scanner.Err())
	}
}

func makeTables() {
	out := fmt.Sprintf("%s\n", originalHeader)
	out += fmt.Sprint("var confusablesMap = map[rune]string{\n\n")
	for _, c := range confusables {
		out += fmt.Sprintf("0x%.8X: %+q,\n", c.k, string(c.v))
	}
	out += fmt.Sprintln("}")

	WriteGoFile("tables.go", "confusables", []byte(out))
}

const header = `// This file was generated by go generate; DO NOT EDIT

package %s

`

func WriteGoFile(filename, pkg string, b []byte) {
	w, err := os.Create(filename)
	if err != nil {
		log.Fatalf("Could not create file %s: %v", filename, err)
	}
	defer w.Close()
	_, err = fmt.Fprintf(w, header, pkg)
	if err != nil {
		log.Fatalf("Error writing header: %v", err)
	}
	// Strip leading newlines.
	for len(b) > 0 && b[0] == '\n' {
		b = b[1:]
	}
	formatted, err := format.Source(b)
	if err != nil {
		// Print the original buffer even in case of an error so that the
		// returned error can be meaningfully interpreted.
		w.Write(b)
		log.Fatalf("Error formatting file %s: %v", filename, err)
	}
	if _, err := w.Write(formatted); err != nil {
		log.Fatalf("Error writing file %s: %v", filename, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"strconv"
	"time"

	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/tabs"
	"github.com/MJKWoolnough/gopherjs/xdom"
	"github.com/MJKWoolnough/gopherjs/xform"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/MJKWoolnough/minewebgen/internal/data"
	"honnef.co/go/js/dom"
)

func mapsTab(c dom.Element) {
	xjs.RemoveChildren(c)
	c.AppendChild(xjs.SetInnerText(xdom.H2(), "Maps"))
	nm := xdom.Button()
	c.AppendChild(xjs.SetInnerText(nm, "New Map"))
	nm.AddEventListener("click", false, func(dom.Event) {
		d := xdom.Div()
		o := overlay.New(d)
		o.OnClose(func() {
			go mapsTab(c)
		})
		xjs.AppendChildren(d,
			xjs.SetInnerText(xdom.H1(), "New Map"),
			tabs.New([]tabs.Tab{
				{"Create", createMap(o)},
				{"Upload/Download", func(c dom.Element) {
					c.AppendChild(transferFile("Map", "Upload/Download", 1, o))
				}},
				{"Generate", func(c dom.Element) {
					c.AppendChild(transferFile("Map", "Generate", 2, o))
				}},
			}),
		)
		xjs.Body().AppendChild(o)
	})
	m, err := RPC.MapList()
	if err != nil {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), err.Error()))
		return
	}
	if len(m) == 0 {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), "No Maps"))
		return
	}
	t := xjs.AppendChildren(xdom.Table(), xjs.AppendChildren(xdom.Thead(), xjs.AppendChildren(xdom.Tr(),
		xjs.SetInnerText(xdom.Th(), "Map Name"),
	)))

	for _, mp := range m {
		name := xjs.SetInnerText(xdom.Td(), mp.Name)
		name.AddEventListener("click", false, func() func(dom.Event) {
			m := mp
			return func(dom.Event) {
				o := overlay.New(xjs.AppendChildren(xdom.Div(), tabs.New([]tabs.Tab{
					{"General", mapGeneral(m)},
					{"Properties", mapProperties(m)},
				})))
				o.OnClose(func() {
					mapsTab(c)
				})
				xjs.Body().AppendChild(o)
			}
		}())
		t.AppendChild(xjs.AppendChildren(xdom.Tr(), name))
	}
	c.AppendChild(t)
}

func createMap(o *overlay.Overlay) func(dom.Element) {
	return func(c dom.Element) {

	}
}

func mapGeneral(m data.Map) func(dom.Element) {
	return func(c dom.Element) {
		go func() {
			servers, err := RPC.ServerList()
			if err != nil {
				c.AppendChild(xjs.SetInnerText(xdom.Div(), "Error getting server list: "+err.Error()))
				return
			}
			name := xform.InputText("name", m.Name)
			name.Required = true
			opts := make([]xform.Option, 1, len(servers)+1)
			opts[0] = xform.Option{
				Label:    "-- None -- ",
				Value:    "-1",
				Selected: m.Server == -1,
			}
			for i, s := range servers {
				n := s.Name
				if s.Map == -1 {
					if s.ID == m.Server {
						n += "* - " + n
					} else {
						n += "! - " + n
					}
				} else {
					n = "    " + n
				}
				opts = append(opts, xform.Option{
					Label:    n,
					Value:    strconv.Itoa(i),
					Selected: s.ID == m.Server,
				})
			}
			sel := xform.SelectBox("server", opts...)
			submit := xform.InputSubmit("Set")
			submit.AddEventListener("click", false, func(e dom.Event) {
				if name.Value == "" {
					return
				}
				mID, err := strconv.Atoi(sel.Value)
				if err != nil || mID < -1 || mID >= len(servers) {
					return
				}
				submit.Disabled = true
				e.PreventDefault()
				if mID >= 0 {
					s := servers[mID]
					mID = s.ID
				}
				go func() {
					err = RPC.SetServerMap(mID, m.ID)
					if err != nil {
						xjs.Alert("Error setting map server: %s", err)
						return
					}
					m.Name = name.Value
					err = RPC.SetMap(m)
					if err != nil {
						xjs.Alert("Error setting map data: %s", err)
						return
					}
					span := xdom.Span()
					span.Style().Set("color", "#f00")
					c.AppendChild(xjs.SetInnerText(span, "Saved!"))
					time.Sleep(5 * time.Second)
					c.RemoveChild(span)
					submit.Disabled = false
				}()
			})
			xjs.AppendChildren(c, xjs.AppendChildren(xdom.Form(),
				xform.Label("Server Name", "name"),
				name,
				xdom.Br(),
				xform.Label("Server Name", "server"),
				sel,
				xdom.Br(),
				submit,
			))
		}()
	}
}

func mapProperties(m data.Map) func(dom.Element) {
	return func(c dom.Element) {
		go editProperties(c, "Map", m.ID, RPC.MapProperties, RPC.SetMapProperties)
	}
}
<commit_msg>Implemented simple map creation and SuperFlat creation<commit_after>package main

import (
	"errors"
	"strconv"
	"time"

	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/tabs"
	"github.com/MJKWoolnough/gopherjs/xdom"
	"github.com/MJKWoolnough/gopherjs/xform"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/MJKWoolnough/minewebgen/internal/data"
	"honnef.co/go/js/dom"
)

func mapsTab(c dom.Element) {
	xjs.RemoveChildren(c)
	c.AppendChild(xjs.SetInnerText(xdom.H2(), "Maps"))
	nm := xdom.Button()
	c.AppendChild(xjs.SetInnerText(nm, "New Map"))
	nm.AddEventListener("click", false, func(dom.Event) {
		d := xdom.Div()
		o := overlay.New(d)
		o.OnClose(func() {
			go mapsTab(c)
		})
		xjs.AppendChildren(d,
			xjs.SetInnerText(xdom.H1(), "New Map"),
			tabs.New([]tabs.Tab{
				{"Create", createMap(o)},
				{"Upload/Download", func(c dom.Element) {
					c.AppendChild(transferFile("Map", "Upload/Download", 1, o))
				}},
				{"Generate", func(c dom.Element) {
					c.AppendChild(transferFile("Map", "Generate", 2, o))
				}},
			}),
		)
		xjs.Body().AppendChild(o)
	})
	m, err := RPC.MapList()
	if err != nil {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), err.Error()))
		return
	}
	if len(m) == 0 {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), "No Maps"))
		return
	}
	t := xjs.AppendChildren(xdom.Table(), xjs.AppendChildren(xdom.Thead(), xjs.AppendChildren(xdom.Tr(),
		xjs.SetInnerText(xdom.Th(), "Map Name"),
	)))

	for _, mp := range m {
		name := xjs.SetInnerText(xdom.Td(), mp.Name)
		name.AddEventListener("click", false, func() func(dom.Event) {
			m := mp
			return func(dom.Event) {
				o := overlay.New(xjs.AppendChildren(xdom.Div(), tabs.New([]tabs.Tab{
					{"General", mapGeneral(m)},
					{"Properties", mapProperties(m)},
				})))
				o.OnClose(func() {
					mapsTab(c)
				})
				xjs.Body().AppendChild(o)
			}
		}())
		t.AppendChild(xjs.AppendChildren(xdom.Tr(), name))
	}
	c.AppendChild(t)
}

var gameModes = [...]string{"Survival", "Creative", "Adventure", "Hardcore", "Spectator"}

func createMap(o *overlay.Overlay) func(dom.Element) {
	return func(c dom.Element) {
		name := xform.InputText("name", "")
		name.Required = true
		gmOpts := make([]xform.Option, len(gameModes))
		for i, m := range gameModes {
			gmOpts[i] = xform.Option{
				Label: m,
				Value: strconv.Itoa(i),
			}
		}
		gameMode := xform.SelectBox("gamemode", gmOpts...)
		seed := xform.InputText("seed", "")
		structures := xform.InputCheckbox("structures", true)
		cheats := xform.InputCheckbox("cheats", false)
		fs := xdom.Fieldset()
		fs.AppendChild(xjs.SetInnerText(xdom.Legend(), "Create Map"))
		c.AppendChild(xjs.AppendChildren(xdom.Form(), fs))
		dataParser := func(mode int) func() (data.DefaultMap, error) {
			return func() (data.DefaultMap, error) {
				data := data.DefaultMap{
					Mode: mode,
				}
				var err error
				data.Name = name.Value
				si := gameMode.SelectedIndex
				if si < 0 || si >= len(gameModes) {
					return data, errors.New("invalid gamemode")
				}
				if seed.Value == "" {
					seed.Value = "0"
				}
				data.Seed, err = strconv.ParseInt(seed.Value, 10, 64)
				if err != nil {
					return data, err
				}
				data.Structures = structures.Checked
				data.Cheats = cheats.Checked
				return data, nil
			}
		}
		xjs.AppendChildren(fs,
			xform.Label("Name", "name"),
			name,
			xdom.Br(),
			xform.Label("Game Mode", "gamemode"),
			gameMode,
			xdom.Br(),
			xform.Label("Level Seed", "seed"),
			seed,
			xdom.Br(),
			xform.Label("Structures", "structures"),
			structures,
			xdom.Br(),
			xform.Label("Cheats", "cheats"),
			cheats,
			xdom.Br(),
			tabs.New([]tabs.Tab{
				{"Default", createMapMode(0, o, dataParser(0))},
				{"Super Flat", createSuperFlatMap(o, dataParser(1))},
				{"Large Biomes", createMapMode(2, o, dataParser(2))},
				{"Amplified", createMapMode(3, o, dataParser(3))},
				{"Customised", createCustomisedMap(o, dataParser(4))},
			}),
		)
	}
}

var worldTypes = [...]string{
	"The standard minecraft map generation.",
	"A simple generator allowing customised levels of blocks.",
	"The standard minecraft map generation, but tweaked to allow for much larger biomes.",
	"The standard minecraft map generation, but tweaked to stretch the land upwards.",
	"A completely customiseable generator.",
}

func createMapMode(mode int, o *overlay.Overlay, dataParser func() (data.DefaultMap, error)) func(dom.Element) {
	submit := xform.InputSubmit("Create Map")
	submit.AddEventListener("click", false, func(e dom.Event) {
		data, err := dataParser()
		if err != nil {
			xjs.Alert("Error parsing values: %s", err)
			return
		}
		e.PreventDefault()
		go func() {
			err = RPC.CreateDefaultMap(data)
			if err != nil {
				xjs.Alert("Error parsing values: %s", err)
				return
			}
			o.Close()

		}()
	})
	return func(c dom.Element) {
		xjs.AppendChildren(c,
			xjs.SetPreText(xdom.Div(), worldTypes[mode]),
			xdom.Br(),
			submit,
		)
	}
}

func createSuperFlatMap(o *overlay.Overlay, dataParser func() (data.DefaultMap, error)) func(dom.Element) {
	// create better UI here
	d := xdom.Div()
	gs := xform.InputText("settings", "0")
	submit := xform.InputSubmit("Create Map")
	xjs.AppendChildren(d,
		xjs.SetPreText(xdom.Div(), worldTypes[1]),
		xform.Label("Generator Settings", "settings"),
		gs,
		xdom.Br(),
		submit,
	)
	submit.AddEventListener("click", false, func(e dom.Event) {
		d, err := dataParser()
		if err != nil {
			xjs.Alert("Error parsing values: %s", err)
			return
		}
		e.PreventDefault()
		go func() {
			err = RPC.CreateSuperflatMap(data.SuperFlatMap{
				DefaultMap:        d,
				GeneratorSettings: gs.Value,
			})
			if err != nil {
				xjs.Alert("Error parsing values: %s", err)
				return
			}
			o.Close()

		}()
	})
	return func(c dom.Element) {
		c.AppendChild(d)
	}
}

func createCustomisedMap(o *overlay.Overlay, dataParser func() (data.DefaultMap, error)) func(dom.Element) {
	d := xdom.Div()
	return func(c dom.Element) {
		c.AppendChild(d)
	}
}

func mapGeneral(m data.Map) func(dom.Element) {
	return func(c dom.Element) {
		go func() {
			servers, err := RPC.ServerList()
			if err != nil {
				c.AppendChild(xjs.SetInnerText(xdom.Div(), "Error getting server list: "+err.Error()))
				return
			}
			name := xform.InputText("name", m.Name)
			name.Required = true
			opts := make([]xform.Option, 1, len(servers)+1)
			opts[0] = xform.Option{
				Label:    "-- None --",
				Value:    "-1",
				Selected: m.Server == -1,
			}
			for i, s := range servers {
				n := s.Name
				if s.Map == -1 {
					if s.ID == m.Server {
						n += "* - " + n
					} else {
						n += "! - " + n
					}
				} else {
					n = "    " + n
				}
				opts = append(opts, xform.Option{
					Label:    n,
					Value:    strconv.Itoa(i),
					Selected: s.ID == m.Server,
				})
			}
			sel := xform.SelectBox("server", opts...)
			submit := xform.InputSubmit("Set")
			submit.AddEventListener("click", false, func(e dom.Event) {
				if name.Value == "" {
					return
				}
				mID, err := strconv.Atoi(sel.Value)
				if err != nil || mID < -1 || mID >= len(servers) {
					return
				}
				submit.Disabled = true
				e.PreventDefault()
				if mID >= 0 {
					s := servers[mID]
					mID = s.ID
				}
				go func() {
					err = RPC.SetServerMap(mID, m.ID)
					if err != nil {
						xjs.Alert("Error setting map server: %s", err)
						return
					}
					m.Name = name.Value
					err = RPC.SetMap(m)
					if err != nil {
						xjs.Alert("Error setting map data: %s", err)
						return
					}
					span := xdom.Span()
					span.Style().Set("color", "#f00")
					c.AppendChild(xjs.SetInnerText(span, "Saved!"))
					time.Sleep(5 * time.Second)
					c.RemoveChild(span)
					submit.Disabled = false
				}()
			})
			xjs.AppendChildren(c, xjs.AppendChildren(xdom.Form(),
				xform.Label("Server Name", "name"),
				name,
				xdom.Br(),
				xform.Label("Server Name", "server"),
				sel,
				xdom.Br(),
				submit,
			))
		}()
	}
}

func mapProperties(m data.Map) func(dom.Element) {
	return func(c dom.Element) {
		go editProperties(c, "Map", m.ID, RPC.MapProperties, RPC.SetMapProperties)
	}
}
<|endoftext|>


<commit_before>package apptail

import (
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/logyard-apps/apptail/docker"
	"github.com/ActiveState/logyard-apps/apptail/event"
	"github.com/ActiveState/logyard-apps/apptail/message"
	"github.com/ActiveState/logyard-apps/apptail/pubchannel"
	"github.com/ActiveState/logyard-apps/apptail/util"
	"github.com/ActiveState/logyard-apps/common"
	"github.com/ActiveState/logyard-apps/sieve"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Instance is the NATS message sent by dea_ng to notify of new instances.
type Instance struct {
	AppGUID  string
	AppName  string
	AppSpace string
	Type     string
	Index    int
	DockerId string `json:"docker_id"`
	RootPath string
	LogFiles map[string]string
	Streams  bool
	pubch    *pubchannel.PubChannel
}

func (instance *Instance) Identifier() string {
	return fmt.Sprintf("%v[%v:%v]", instance.AppName, instance.Index, instance.DockerId[:docker.ID_LENGTH])
}

// Tail begins tailing the files for this instance.
func (instance *Instance) Tail() {
	log.Infof("Tailing %v logs for %v -- %+v",
		instance.Type, instance.Identifier(), instance)

	stopCh := make(chan bool)

	instance.pubch = pubchannel.New("event.timeline", stopCh)

	logfiles := instance.getLogFiles()

	log.Infof("Determined log files: %+v", logfiles)

	for name, filename := range logfiles {
		go instance.tailFile(name, filename, stopCh)
	}

	if instance.Streams {
		go instance.tailStream("stdout", stopCh)
		go instance.tailStream("stderr", stopCh)
	}

	go func() {
		docker.DockerListener.BlockUntilContainerStops(instance.DockerId)
		log.Infof("Container for %v exited", instance.Identifier())
		close(stopCh)
	}()
}

func (instance *Instance) tailFile(name, filename string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, name, filename)
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	rateLimiter := GetConfig().GetLeakyBucket()

	t, err := tail.TailFile(filename, tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   true,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		RateLimiter: rateLimiter})
	if err != nil {
		log.Warnf("Cannot tail file (%s); %s", filename, err)
		instance.SendTimelineEvent("ERROR -- Cannot tail file (%s); %s", name, err)
		return
	}

	instance.readFromTail(t, pub, name, stopCh)
}

func (instance *Instance) readFromTail(t *tail.Tail, pub *zmqpubsub.Publisher, name string, stopCh chan bool) {
	var err error
FORLOOP:
	for {
		select {
		case line, ok := <-t.Lines:
			if !ok {
				err = t.Wait()
				break FORLOOP
			}
			instance.publishLine(pub, name, line)
		case <-stopCh:
			err = t.Stop()
			break FORLOOP
		}
	}

	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- Error tailing file (%s); %s", name, err)
	}

	log.Infof("Completed tailing %v log for %v", name, instance.Identifier())
}

func (instance *Instance) tailStream(stream string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, stream, "")
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	rateLimiter := GetConfig().GetLeakyBucket()

	reqUrl, err := url.Parse(fmt.Sprintf("http://localhost:4243/containers/%s/logs", instance.DockerId))
	if err != nil {
		log.Warn(err)
		return
	}
	q := reqUrl.Query()
	q.Set(stream, "true")
	q.Set("follow", "true")
	reqUrl.RawQuery = q.Encode()

	resp, err := http.Get(reqUrl.String())
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		log.Warnf("HTTP error response %v from %v", resp.Status, reqUrl)
		return
	}

	t, err := tail.TailReader(util.WrapReadSeekClose(resp.Body), tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   false,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		RateLimiter: rateLimiter})
	if err != nil {
		log.Warnf("Cannot tail docker stream (%s); %s", stream, err)
		instance.SendTimelineEvent("ERROR -- Cannot tail file (%s); %s", stream, err)
		return
	}

	instance.readFromTail(t, pub, stream, stopCh)
}

func (instance *Instance) getLogFiles() map[string]string {
	var logfiles map[string]string

	rawMode := len(instance.LogFiles) > 0
	if rawMode {
		// If the logfiles list was explicitly passed, use it as is.
		logfiles = instance.LogFiles
	} else {
		// Use $STACKATO_LOG_FILES
		logfiles = make(map[string]string)
		if env, err := docker.GetDockerAppEnv(instance.RootPath); err != nil {
			log.Errorf("Failed to read docker image env: %v", err)
		} else {
			if s, ok := env["STACKATO_LOG_FILES"]; ok {
				parts := strings.Split(s, ":")
				if len(parts) > 7 {
					parts = parts[len(parts)-7 : len(parts)]
					instance.SendTimelineEvent("WARN -- $STACKATO_LOG_FILES is large; using only last 7 logs: %v", parts)
				}
				for _, f := range parts {
					parts := strings.SplitN(f, "=", 2)
					logfiles[parts[0]] = parts[1]
				}
			}
		}
	}

	// Expand paths, and securely ensure they fall within the app root.
	logfilesSecure := make(map[string]string)
	for name, path := range logfiles {
		var fullpath string

		// Treat relative paths as being relative to $STACKATO_APP_ROOT
		if !filepath.IsAbs(path) {
			stackatoAppRoot := "/home/stackato/"
			fullpath = filepath.Join(instance.RootPath, stackatoAppRoot, path)
		} else {
			fullpath = filepath.Join(instance.RootPath, path)
		}

		fullpath, err := filepath.Abs(fullpath)
		if err != nil {
			log.Warnf("Cannot find Abs of %v <join> %v: %v", instance.RootPath, path, err)
			instance.SendTimelineEvent("WARN -- Failed to find absolute path for %v", path)
			continue
		}
		fullpath, err = filepath.EvalSymlinks(fullpath)
		if err != nil {
			log.Infof("Error reading log file %v: %v", fullpath, err)
			instance.SendTimelineEvent("WARN -- Ignoring missing/inaccessible path %v", path)
			continue
		}
		if !strings.HasPrefix(fullpath, instance.RootPath) {
			log.Warnf("Ignoring insecure log path %v (via %v) in instance %+v", fullpath, path, instance)
			// This user warning is exactly the same as above, lest we provide
			// a backdoor for a malicious user to list the directory tree on
			// the host.
			instance.SendTimelineEvent("WARN -- Ignoring missing/inaccessible path %v", path)
			continue
		}
		logfilesSecure[name] = fullpath
	}

	if len(logfilesSecure) == 0 && !instance.Streams {
		instance.SendTimelineEvent("ERROR -- No valid log files detected for tailing")
	}

	return logfilesSecure
}

func (instance *Instance) getReadLimit(
	pub *zmqpubsub.Publisher,
	logname string,
	filename string) (int64, error) {
	// convert MB to limit in bytes.
	filesizeLimit := GetConfig().FileSizeLimit * 1024 * 1024
	if !(filesizeLimit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	limit := filesizeLimit
	if len(filename) != 0 {
		fi, err := os.Stat(filename)
		if err != nil {
			return -1, fmt.Errorf("Cannot stat file (%s); %s", filename, err)
		}
		size := fi.Size()
		if size > filesizeLimit {
			instance.SendTimelineEvent(
				"Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
				logname, size, filesizeLimit)
		} else {
			limit = size
		}
	}
	return limit, nil
}

// publishLine publishes a log line corresponding to this instance.
func (instance *Instance) publishLine(pub *zmqpubsub.Publisher, logname string, line *tail.Line) {
	instance.publishLineAs(pub, instance.Type, logname, line)
}

func (instance *Instance) publishLineAs(pub *zmqpubsub.Publisher, source string, logname string, line *tail.Line) {
	if line == nil {
		panic("line is nil")
	}

	msg := &message.Message{
		LogFilename:   logname,
		Source:        source,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		MessageCommon: common.NewMessageCommon(line.Text, line.Time, util.LocalNodeId()),
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = fmt.Sprintf("%v[apptail]", util.GetBrandName())
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		common.Fatal("Unable to publish: %v", err)
	}
}

func (instance *Instance) SendTimelineEvent(format string, v ...interface{}) {
	line := fmt.Sprintf(format, v...)
	tEvent := event.TimelineEvent{event.App{instance.AppGUID, instance.AppSpace, instance.AppName}, instance.Index}
	evt := sieve.Event{
		Type:     "timeline",
		Desc:     line,
		Severity: "INFO",
		Info: map[string]interface{}{
			"app":            tEvent.App,
			"instance_index": tEvent.InstanceIndex,
		},
		Process:       "apptail",
		MessageCommon: common.NewMessageCommon(line, time.Now(), util.LocalNodeId()),
	}
	instance.pubch.Ch <- evt
}
<commit_msg>300085 Rename `Streams` to `DockerStreams` to be more explicit<commit_after>package apptail

import (
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/logyard-apps/apptail/docker"
	"github.com/ActiveState/logyard-apps/apptail/event"
	"github.com/ActiveState/logyard-apps/apptail/message"
	"github.com/ActiveState/logyard-apps/apptail/pubchannel"
	"github.com/ActiveState/logyard-apps/apptail/util"
	"github.com/ActiveState/logyard-apps/common"
	"github.com/ActiveState/logyard-apps/sieve"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Instance is the NATS message sent by dea_ng to notify of new instances.
type Instance struct {
	AppGUID       string
	AppName       string
	AppSpace      string
	Type          string
	Index         int
	DockerId      string `json:"docker_id"`
	RootPath      string
	LogFiles      map[string]string
	DockerStreams bool `json:"docker_streams"`
	pubch         *pubchannel.PubChannel
}

func (instance *Instance) Identifier() string {
	return fmt.Sprintf("%v[%v:%v]", instance.AppName, instance.Index, instance.DockerId[:docker.ID_LENGTH])
}

// Tail begins tailing the files for this instance.
func (instance *Instance) Tail() {
	log.Infof("Tailing %v logs for %v -- %+v",
		instance.Type, instance.Identifier(), instance)

	stopCh := make(chan bool)

	instance.pubch = pubchannel.New("event.timeline", stopCh)

	logfiles := instance.getLogFiles()

	log.Infof("Determined log files: %+v", logfiles)

	for name, filename := range logfiles {
		go instance.tailFile(name, filename, stopCh)
	}

	if instance.DockerStreams {
		go instance.tailStream("stdout", stopCh)
		go instance.tailStream("stderr", stopCh)
	}

	go func() {
		docker.DockerListener.BlockUntilContainerStops(instance.DockerId)
		log.Infof("Container for %v exited", instance.Identifier())
		close(stopCh)
	}()
}

func (instance *Instance) tailFile(name, filename string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, name, filename)
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	rateLimiter := GetConfig().GetLeakyBucket()

	t, err := tail.TailFile(filename, tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   true,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		RateLimiter: rateLimiter})
	if err != nil {
		log.Warnf("Cannot tail file (%s); %s", filename, err)
		instance.SendTimelineEvent("ERROR -- Cannot tail file (%s); %s", name, err)
		return
	}

	instance.readFromTail(t, pub, name, stopCh)
}

func (instance *Instance) readFromTail(t *tail.Tail, pub *zmqpubsub.Publisher, name string, stopCh chan bool) {
	var err error
FORLOOP:
	for {
		select {
		case line, ok := <-t.Lines:
			if !ok {
				err = t.Wait()
				break FORLOOP
			}
			instance.publishLine(pub, name, line)
		case <-stopCh:
			err = t.Stop()
			break FORLOOP
		}
	}

	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- Error tailing file (%s); %s", name, err)
	}

	log.Infof("Completed tailing %v log for %v", name, instance.Identifier())
}

func (instance *Instance) tailStream(stream string, stopCh chan bool) {
	var err error

	pub := logyard.Broker.NewPublisherMust()
	defer pub.Stop()

	limit, err := instance.getReadLimit(pub, stream, "")
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	rateLimiter := GetConfig().GetLeakyBucket()

	reqUrl, err := url.Parse(fmt.Sprintf("http://localhost:4243/containers/%s/logs", instance.DockerId))
	if err != nil {
		log.Warn(err)
		return
	}
	q := reqUrl.Query()
	q.Set(stream, "true")
	q.Set("follow", "true")
	reqUrl.RawQuery = q.Encode()

	resp, err := http.Get(reqUrl.String())
	if err != nil {
		log.Warn(err)
		instance.SendTimelineEvent("WARN -- %v", err)
		return
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		log.Warnf("HTTP error response %v from %v", resp.Status, reqUrl)
		return
	}

	t, err := tail.TailReader(util.WrapReadSeekClose(resp.Body), tail.Config{
		MaxLineSize: GetConfig().MaxRecordSize,
		MustExist:   false,
		Follow:      true,
		Location:    &tail.SeekInfo{-limit, os.SEEK_END},
		ReOpen:      false,
		Poll:        false,
		RateLimiter: rateLimiter})
	if err != nil {
		log.Warnf("Cannot tail docker stream (%s); %s", stream, err)
		instance.SendTimelineEvent("ERROR -- Cannot tail file (%s); %s", stream, err)
		return
	}

	instance.readFromTail(t, pub, stream, stopCh)
}

func (instance *Instance) getLogFiles() map[string]string {
	var logfiles map[string]string

	rawMode := len(instance.LogFiles) > 0
	if rawMode {
		// If the logfiles list was explicitly passed, use it as is.
		logfiles = instance.LogFiles
	} else {
		// Use $STACKATO_LOG_FILES
		logfiles = make(map[string]string)
		if env, err := docker.GetDockerAppEnv(instance.RootPath); err != nil {
			log.Errorf("Failed to read docker image env: %v", err)
		} else {
			if s, ok := env["STACKATO_LOG_FILES"]; ok {
				parts := strings.Split(s, ":")
				if len(parts) > 7 {
					parts = parts[len(parts)-7 : len(parts)]
					instance.SendTimelineEvent("WARN -- $STACKATO_LOG_FILES is large; using only last 7 logs: %v", parts)
				}
				for _, f := range parts {
					parts := strings.SplitN(f, "=", 2)
					logfiles[parts[0]] = parts[1]
				}
			}
		}
	}

	// Expand paths, and securely ensure they fall within the app root.
	logfilesSecure := make(map[string]string)
	for name, path := range logfiles {
		var fullpath string

		// Treat relative paths as being relative to $STACKATO_APP_ROOT
		if !filepath.IsAbs(path) {
			stackatoAppRoot := "/home/stackato/"
			fullpath = filepath.Join(instance.RootPath, stackatoAppRoot, path)
		} else {
			fullpath = filepath.Join(instance.RootPath, path)
		}

		fullpath, err := filepath.Abs(fullpath)
		if err != nil {
			log.Warnf("Cannot find Abs of %v <join> %v: %v", instance.RootPath, path, err)
			instance.SendTimelineEvent("WARN -- Failed to find absolute path for %v", path)
			continue
		}
		fullpath, err = filepath.EvalSymlinks(fullpath)
		if err != nil {
			log.Infof("Error reading log file %v: %v", fullpath, err)
			instance.SendTimelineEvent("WARN -- Ignoring missing/inaccessible path %v", path)
			continue
		}
		if !strings.HasPrefix(fullpath, instance.RootPath) {
			log.Warnf("Ignoring insecure log path %v (via %v) in instance %+v", fullpath, path, instance)
			// This user warning is exactly the same as above, lest we provide
			// a backdoor for a malicious user to list the directory tree on
			// the host.
			instance.SendTimelineEvent("WARN -- Ignoring missing/inaccessible path %v", path)
			continue
		}
		logfilesSecure[name] = fullpath
	}

	if len(logfilesSecure) == 0 && !instance.DockerStreams {
		instance.SendTimelineEvent("ERROR -- No valid log files detected for tailing")
	}

	return logfilesSecure
}

func (instance *Instance) getReadLimit(
	pub *zmqpubsub.Publisher,
	logname string,
	filename string) (int64, error) {
	// convert MB to limit in bytes.
	filesizeLimit := GetConfig().FileSizeLimit * 1024 * 1024
	if !(filesizeLimit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	limit := filesizeLimit
	if len(filename) != 0 {
		fi, err := os.Stat(filename)
		if err != nil {
			return -1, fmt.Errorf("Cannot stat file (%s); %s", filename, err)
		}
		size := fi.Size()
		if size > filesizeLimit {
			instance.SendTimelineEvent(
				"Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
				logname, size, filesizeLimit)
		} else {
			limit = size
		}
	}
	return limit, nil
}

// publishLine publishes a log line corresponding to this instance.
func (instance *Instance) publishLine(pub *zmqpubsub.Publisher, logname string, line *tail.Line) {
	instance.publishLineAs(pub, instance.Type, logname, line)
}

func (instance *Instance) publishLineAs(pub *zmqpubsub.Publisher, source string, logname string, line *tail.Line) {
	if line == nil {
		panic("line is nil")
	}

	msg := &message.Message{
		LogFilename:   logname,
		Source:        source,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		MessageCommon: common.NewMessageCommon(line.Text, line.Time, util.LocalNodeId()),
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = fmt.Sprintf("%v[apptail]", util.GetBrandName())
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		common.Fatal("Unable to publish: %v", err)
	}
}

func (instance *Instance) SendTimelineEvent(format string, v ...interface{}) {
	line := fmt.Sprintf(format, v...)
	tEvent := event.TimelineEvent{event.App{instance.AppGUID, instance.AppSpace, instance.AppName}, instance.Index}
	evt := sieve.Event{
		Type:     "timeline",
		Desc:     line,
		Severity: "INFO",
		Info: map[string]interface{}{
			"app":            tEvent.App,
			"instance_index": tEvent.InstanceIndex,
		},
		Process:       "apptail",
		MessageCommon: common.NewMessageCommon(line, time.Now(), util.LocalNodeId()),
	}
	instance.pubch.Ch <- evt
}
<|endoftext|>


<commit_before>/*
Package key contains a single Key interface. The key package was
created to prevent cicular depedencies betwee "github.com/lleo/go-hamt" and
either "github.com/lleo/go-hamt/hamt32" or "github.com/lleo/go-hamt/hamt64".

However the key pacakge is also used by the functional HAMT variation in
"github.com/lleo/go-hamt-functional".
*/
package key

import (
	"fmt"
	"hash/fnv"
)

type Key interface {
	String() string // ISA/statisfies fmt.Stringer interface
	Equals(Key) bool
	Hash30() uint32
	Hash60() uint64
}

// Base struct of all structs that satisfy Key interface
type KeyBase struct {
	hash30 uint32
	hash60 uint64
}

// INTERESTING IDEA JUST NOT YET...
//
// type Hash30 uint32
//
// func (h30 Hash30) String() {
// 	return hash30String(uint32(h30))
// }
//
// func (h30 Hash30) index(depth uint) {
// 	return index(uint32(h30), depth)
// }
//
// type Hash60 uint64
//
// func (h Hash60) String() {
// 	return hash60String(uint64(h))
// }
//
// func (h60 Hash60) index(depth uint) {
// 	return index(uint64(h60), depth)
// }

////
//// 30bit hash values & functions
////
//
//const NBITS30 uint = 5
//
//func indexMask30(depth uint) uint32 {
//	return uint32(uint8(1<<NBITS30)-1) << (depth * NBITS30)
//}
//
//func index30(h30 uint32, depth uint) uint {
//	var idxMask = indexMask30(depth)
//	var idx = uint((h30 & idxMask) >> (depth * NBITS30))
//	return idx
//}
//
//func hashPathString30(hashPath uint32, depth uint) string {
//	if depth == 0 {
//		return "/"
//	}
//	var strs = make([]string, depth)
//
//	for d := depth; d > 0; d-- {
//		var idx = index30(hashPath, d-1)
//		strs[d-1] = fmt.Sprintf("%02d", idx)
//	}
//
//	return "/" + strings.Join(strs, "/")
//}
//
//func hash30String(h30 uint32) string {
//	return hashPathString30(h30, 6)
//}
//
//func hash30String(h30 uint32) string {
//	return hashPathString30(h30, 6)
//}
//
////
//// 60bit hash values & functions
////
//
//const NBITS60 uint = 6
//
//func indexMask60(depth uint) uint64 {
//	return uint64(uint8(1<<NBITS60)-1) << (depth * NBITS60)
//}
//
//func index60(h60 uint64, depth uint) uint {
//	var idxMask = indexMask60(depth)
//	var idx = uint((h60 & idxMask) >> (depth * NBITS60))
//	return idx
//}
//
//func hashPathString60(hashPath uint64, depth uint) string {
//	if depth == 0 {
//		return "/"
//	}
//	var strs = make([]string, depth)
//
//	for d := depth; d > 0; d-- {
//		var idx = index60(hashPath, d-1)
//		strs[d-1] = fmt.Sprintf("%02d", idx)
//	}
//
//	return "/" + strings.Join(strs, "/")
//}
//
//func hash60String(h60 uint64) string {
//	return hashPathString60(h60, 6)
//}
//
//func hash60String(h60 uint64) string {
//	return hashPathString60(h60, 6)
//}

const mask30 = uint32(1<<30) - 1

// Fold the top two bits into the bottom 30 to get 30bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold30(h32 uint32) uint32 {
	return h32>>30 ^ h32&mask30
}

func hash32(bs []byte) uint32 {
	var h = fnv.New32()
	h.Write(bs)
	return h.Sum32()
}

const mask60 = uint64(1<<60) - 1

// Fold the top four bits into the bottom 60 to get 60bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold60(h64 uint64) uint64 {
	return h64>>60 ^ h64&mask60
}

func hash64(bs []byte) uint64 {
	var h = fnv.New64()
	h.Write(bs)
	return h.Sum64()
}

func (kb *KeyBase) String() string {
	return fmt.Sprintf("KeyBase{hash30:%d}", kb.hash30)
}

// Initialize the KeyBase part of any struct that has the KeyBase struct embeded.
// will be called by the New() function of any decendent class.
//     k.Initialize(bs)
// where k is interpreted as a *KeyBase and bs is a unique []byte to calculate
// the 30bit hash from.
func (kb *KeyBase) Initialize(bs []byte) {
	kb.hash30 = fold30(hash32(bs))
	kb.hash60 = fold60(hash64(bs))
}

func (kb KeyBase) Hash30() uint32 {
	return kb.hash30
}

func (kb KeyBase) Hash60() uint64 {
	return kb.hash60
}
<commit_msg>Chamged keyBase struct to cache calculation of 60 bit hash in keyBase.hash60. Duplicated all the code to manipulate and stringify the 30bit and 60bit hash values from hamt32 and hamt64.<commit_after>/*
Package key contains a single Key interface. The key package was
created to prevent cicular depedencies betwee "github.com/lleo/go-hamt" and
either "github.com/lleo/go-hamt/hamt32" or "github.com/lleo/go-hamt/hamt64".

However the key pacakge is also used by the functional HAMT variation in
"github.com/lleo/go-hamt-functional".
*/
package key

import (
	"fmt"
	"hash/fnv"
	"strings"
)

type Key interface {
	String() string // ISA/statisfies fmt.Stringer interface
	Equals(Key) bool
	Hash30() uint32
	Hash60() uint64
}

// Base struct of all structs that satisfy Key interface
type KeyBase struct {
	hash30 uint32
	hash60 uint64
}

// INTERESTING IDEA JUST NOT YET...
//
// type Hash30 uint32
//
// func (h30 Hash30) String() {
// 	return hash30String(uint32(h30))
// }
//
// func (h30 Hash30) index(depth uint) {
// 	return index(uint32(h30), depth)
// }
//
// type Hash60 uint64
//
// func (h Hash60) String() {
// 	return hash60String(uint64(h))
// }
//
// func (h60 Hash60) index(depth uint) {
// 	return index(uint64(h60), depth)
// }

//
// 30bit hash values & functions
//

const NBITS30 uint = 5

func indexMask30(depth uint) uint32 {
	return uint32(uint8(1<<NBITS30)-1) << (depth * NBITS30)
}

func index30(h30 uint32, depth uint) uint {
	var idxMask = indexMask30(depth)
	var idx = uint((h30 & idxMask) >> (depth * NBITS30))
	return idx
}

func hashPathString30(hashPath uint32, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index30(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash30String(h30 uint32) string {
	return hashPathString30(h30, 5)
}

//
// 60bit hash values & functions
//

const NBITS60 uint = 6

func indexMask60(depth uint) uint64 {
	return uint64(uint64(1<<NBITS60)-1) << (depth * NBITS60)
}

func index60(h60 uint64, depth uint) uint {
	var idxMask = indexMask60(depth)
	var idx = uint((h60 & idxMask) >> (depth * NBITS60))
	return idx
}

func hashPathString60(hashPath uint64, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index60(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash60String(h60 uint64) string {
	return hashPathString60(h60, 9)
}

const mask30 = uint32(1<<30) - 1

// Fold the top two bits into the bottom 30 to get 30bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold30(h32 uint32) uint32 {
	return h32>>30 ^ h32&mask30
}

func hash32(bs []byte) uint32 {
	var h = fnv.New32()
	h.Write(bs)
	return h.Sum32()
}

const mask60 = uint64(1<<60) - 1

// Fold the top four bits into the bottom 60 to get 60bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold60(h64 uint64) uint64 {
	return h64>>60 ^ h64&mask60
}

func hash64(bs []byte) uint64 {
	var h = fnv.New64()
	h.Write(bs)
	return h.Sum64()
}

func (kb *KeyBase) String() string {
	return fmt.Sprintf("KeyBase{hash30:(%d)%s, hash60:(%d)%s}",
		kb.hash30, hash30String(kb.hash30),
		kb.hash60, hash60String(kb.hash60))
}

// Initialize the KeyBase part of any struct that has the KeyBase struct embeded.
// will be called by the New() function of any decendent class.
//     k.Initialize(bs)
// where k is interpreted as a *KeyBase and bs is a unique []byte to calculate
// the 30bit hash from.
func (kb *KeyBase) Initialize(bs []byte) {
	kb.hash30 = fold30(hash32(bs))
	kb.hash60 = fold60(hash64(bs))
}

func (kb *KeyBase) Hash30() uint32 {
	return kb.hash30
}

func (kb *KeyBase) Hash60() uint64 {
	return kb.hash60
}
<|endoftext|>


<commit_before>/*
Package key contains a single Key interface. The key package was
created to prevent cicular dependencies between "github.com/lleo/go-hamt" and
either "github.com/lleo/go-hamt/hamt32" or "github.com/lleo/go-hamt/hamt64".

Additionally, the github.com/lleo/go-hamt/key provides a Base structure.
The Base structure if added to a derivative key type will provide the
Hamt30() and Hamt60() methods. Base needs to be populated by the derivative
key constructor calling the Initialize([]byte) method.

Any key created using the Key interface must be read only after construction.

The key package is used by the functional HAMT variation in
"github.com/lleo/go-hamt-functional".
*/
package key

import (
	"fmt"
	"hash/fnv"
	"strings"
)

// KeyVal is a simple struct used to transfer lists ([]KeyVal) from one
// function to another.
type KeyVal struct {
	Key Key
	Val interface{}
}

func (kv KeyVal) String() string {
	return fmt.Sprintf("KeyVal{%s, %v}", kv.Key, kv.Val)
}

// Key interface descibes the methods any struct needs to implement to be used
// in either the github.com/lleo/go-hamt or github.com/lleo/go-hamt-functional
// packages.
type Key interface {
	String() string // ISA/statisfies fmt.Stringer interface
	Equals(Key) bool
	Hash30() uint32
	Hash60() uint64
}

// Base struct is intended to be the base struct of all structs that satisfy
// Key interface. It caches the calculated hash30 and hash60 generated when
// the method Initialize([]byte) is called.
type Base struct {
	hash30 uint32
	hash60 uint64
}

// INTERESTING IDEA JUST NOT YET...
//
// type Hash30 uint32
//
// func (h30 Hash30) String() {
// 	return hash30String(uint32(h30))
// }
//
// func (h30 Hash30) index(depth uint) {
// 	return index(uint32(h30), depth)
// }
//
// type Hash60 uint64
//
// func (h Hash60) String() {
// 	return hash60String(uint64(h))
// }
//
// func (h60 Hash60) index(depth uint) {
// 	return index(uint64(h60), depth)
// }

//
// 30bit hash value and string generator functions
// Hash values are generated when Initialize([]byte) is called.
// Hash strings are generated as needed.
//

const nBits30 uint = 5

func indexMask30(depth uint) uint32 {
	return uint32(uint8(1<<nBits30)-1) << (depth * nBits30)
}

func index30(h30 uint32, depth uint) uint {
	var idxMask = indexMask30(depth)
	var idx = uint((h30 & idxMask) >> (depth * nBits30))
	return idx
}

func hashPathString30(hashPath uint32, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index30(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash30String(h30 uint32) string {
	return hashPathString30(h30, 5)
}

//
// 60bit hash value and string generator functions.
// Hash values are generated when Initialize([]byte) is called.
// Hash strings are generated as needed.
//

const nBits60 uint = 6

func indexMask60(depth uint) uint64 {
	return uint64(uint64(1<<nBits60)-1) << (depth * nBits60)
}

func index60(h60 uint64, depth uint) uint {
	var idxMask = indexMask60(depth)
	var idx = uint((h60 & idxMask) >> (depth * nBits60))
	return idx
}

func hashPathString60(hashPath uint64, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index60(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash60String(h60 uint64) string {
	return hashPathString60(h60, 9)
}

const mask30 = uint32(1<<30) - 1

// fold30 folds the top 2 bits into the bottom 30 to get 30bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold30(h32 uint32) uint32 {
	return h32>>30 ^ h32&mask30
}

// hash64 calculates the 64bit hash value of a byte string using hash/fnv.
func hash32(bs []byte) uint32 {
	var h = fnv.New32()
	h.Write(bs)
	return h.Sum32()
}

const mask60 = uint64(1<<60) - 1

// fold60 folds the top 4 bits into the bottom 60 to get 60bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold60(h64 uint64) uint64 {
	return h64>>60 ^ h64&mask60
}

// hash64 calculates the 64bit hash value of a byte string using hash/fnv.
func hash64(bs []byte) uint64 {
	var h = fnv.New64()
	h.Write(bs)
	return h.Sum64()
}

// String provides a human readable form of the Base's hash30 and hash60
// values. Both in raw integer and slash '/' separated integers of the 5 and
// 6 bit path values; for hash30 and hash60 respectively.
func (kb *Base) String() string {
	return fmt.Sprintf("Base{(%d)%s, (%d)%s}",
		kb.hash30, hash30String(kb.hash30),
		kb.hash60, hash60String(kb.hash60))
}

// Initialize the Base part of any struct that has the Base struct embedded.
// will be called by the New() function of any descendant class.
//     k.Initialize(bs)
// where k is interpreted as a *Base and bs is a unique []byte to calculate
// the 30bit hash from.
func (kb *Base) Initialize(bs []byte) {
	kb.hash30 = fold30(hash32(bs))
	kb.hash60 = fold60(hash64(bs))
}

// Hash30 returns the uint32 that contains the 30bit hash value.
func (kb *Base) Hash30() uint32 {
	return kb.hash30
}

// Hash60 returns the uint32 that contains the 60bit hash value.
func (kb *Base) Hash60() uint64 {
	return kb.hash60
}
<commit_msg>reverted to orig method; no on call Hash{30,60}() calculated hash<commit_after>/*
Package key contains a single Key interface. The key package was
created to prevent cicular dependencies between "github.com/lleo/go-hamt" and
either "github.com/lleo/go-hamt/hamt32" or "github.com/lleo/go-hamt/hamt64".

Additionally, the github.com/lleo/go-hamt/key provides a Base structure.
The Base structure if added to a derivative key type will provide the
Hamt30() and Hamt60() methods. Base needs to be populated by the derivative
key constructor calling the Initialize([]byte) method.

Any key created using the Key interface must be read only after construction.

The key package is used by the functional HAMT variation in
"github.com/lleo/go-hamt-functional".
*/
package key

import (
	"fmt"
	"hash/fnv"
	"strings"
)

// KeyVal is a simple struct used to transfer lists ([]KeyVal) from one
// function to another.
type KeyVal struct {
	Key Key
	Val interface{}
}

func (kv KeyVal) String() string {
	return fmt.Sprintf("KeyVal{%s, %v}", kv.Key, kv.Val)
}

// Key interface descibes the methods any struct needs to implement to be used
// in either the github.com/lleo/go-hamt or github.com/lleo/go-hamt-functional
// packages.
type Key interface {
	String() string // ISA/statisfies fmt.Stringer interface
	String30() string
	String60() string
	Equals(Key) bool
	Hash30() uint32
	Hash60() uint64
}

// Base struct is intended to be the base struct of all structs that satisfy
// Key interface. It caches the calculated hash30 and hash60 generated when
// the method Initialize([]byte) is called.
type Base struct {
	hash30 uint32
	hash60 uint64
}

// INTERESTING IDEA JUST NOT YET...
//
// type Hash30 uint32
//
// func (h30 Hash30) String() {
// 	return hash30String(uint32(h30))
// }
//
// func (h30 Hash30) index(depth uint) {
// 	return index(uint32(h30), depth)
// }
//
// type Hash60 uint64
//
// func (h Hash60) String() {
// 	return hash60String(uint64(h))
// }
//
// func (h60 Hash60) index(depth uint) {
// 	return index(uint64(h60), depth)
// }

//
// 30bit hash value and string generator functions
// Hash values are generated when Initialize([]byte) is called.
// Hash strings are generated as needed.
//

const nBits30 uint = 5

func indexMask30(depth uint) uint32 {
	return uint32(uint8(1<<nBits30)-1) << (depth * nBits30)
}

func index30(h30 uint32, depth uint) uint {
	var idxMask = indexMask30(depth)
	var idx = uint((h30 & idxMask) >> (depth * nBits30))
	return idx
}

func hashPathString30(hashPath uint32, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index30(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash30String(h30 uint32) string {
	return hashPathString30(h30, 5)
}

//
// 60bit hash value and string generator functions.
// Hash values are generated when Initialize([]byte) is called.
// Hash strings are generated as needed.
//

const nBits60 uint = 6

func indexMask60(depth uint) uint64 {
	return uint64(uint64(1<<nBits60)-1) << (depth * nBits60)
}

func index60(h60 uint64, depth uint) uint {
	var idxMask = indexMask60(depth)
	var idx = uint((h60 & idxMask) >> (depth * nBits60))
	return idx
}

func hashPathString60(hashPath uint64, depth uint) string {
	if depth == 0 {
		return "/"
	}
	var strs = make([]string, depth+1)

	for d := uint(0); d <= depth; d++ {
		var idx = index60(hashPath, d)
		strs[d] = fmt.Sprintf("%02d", idx)
	}

	return "/" + strings.Join(strs, "/")
}

func hash60String(h60 uint64) string {
	return hashPathString60(h60, 9)
}

const mask30 = uint32(1<<30) - 1

// fold30 folds the top 2 bits into the bottom 30 to get 30bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold30(h32 uint32) uint32 {
	return h32>>30 ^ h32&mask30
}

// hash64 calculates the 64bit hash value of a byte string using hash/fnv.
func hash32(bs []byte) uint32 {
	var h = fnv.New32()
	h.Write(bs)
	return h.Sum32()
}

const mask60 = uint64(1<<60) - 1

// fold60 folds the top 4 bits into the bottom 60 to get 60bits of hash
// from: http://www.isthe.com/chongo/tech/comp/fnv/index.html#xor-fold
func fold60(h64 uint64) uint64 {
	return h64>>60 ^ h64&mask60
}

// hash64 calculates the 64bit hash value of a byte string using hash/fnv.
func hash64(bs []byte) uint64 {
	var h = fnv.New64()
	h.Write(bs)
	return h.Sum64()
}

// String provides a human readable form of the Base's hash30 and hash60
// values. Both in raw integer and slash '/' separated integers of the 5 and
// 6 bit path values; for hash30 and hash60 respectively.
func (kb Base) String() string {
	return fmt.Sprintf("Base{%s, %s}", hash30String(kb.hash30), hash60String(kb.hash60))
}

func (kb Base) String30() string {
	return hash30String(kb.hash30)
}

func (kb Base) String60() string {
	return hash60String(kb.hash60)
}

// Initialize the Base part of any struct that has the Base struct embedded.
// will be called by the New() function of any descendant class.
//     k.Initialize(bs)
// where k is interpreted as a *Base and bs is a unique []byte to calculate
// the 30bit hash from.
func (kb *Base) Initialize(bs []byte) {
	kb.hash30 = fold30(hash32(bs))
	kb.hash60 = fold60(hash64(bs))
}

// Hash30 returns the uint32 that contains the 30bit hash value.
func (kb Base) Hash30() uint32 {
	return kb.hash30
}

// Hash60 returns the uint32 that contains the 60bit hash value.
func (kb Base) Hash60() uint64 {
	return kb.hash60
}
<|endoftext|>


<commit_before>package kickbox

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

type Client struct {
	apiKey string
	http   http.Client
}

func NewClient(apiKey string) Client {
	return Client{
		apiKey: apiKey,
		http:   http.Client{},
	}
}

// Configure the request timeout value (includes connecting, waiting for a response, and reading the response)
func (c Client) SetTimeout(time time.Duration) {
	c.http.Timeout = time
}

func (c Client) Verify(address string) (*Result, error) {
	return c.verify(KickboxResultBuilder{}, c.url(address))
}

func (c Client) url(address string) string {
	return fmt.Sprintf("https://api.kickbox.io/v2/verify?email=%s&apikey=%s", url.QueryEscape(address), url.QueryEscape(c.apiKey))
}

func (c Client) verify(rb ResultBuilder, url string) (*Result, error) {
	// Send our API request
	response, err := c.http.Get(url)
	if err != nil {
		return nil, err
	}

	// Did we get a HTTP 200?
	if response.StatusCode != 200 {
		msg := fmt.Sprintf("Kickbox API returned HTTP %d", response.Status)
		return nil, errors.New(msg)
	}

	// Read the response
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build our Result struct
	result, err := rb.NewResult(body)
	if err != nil {
		return nil, err
	}

	return result, nil
}
<commit_msg>Describe methods<commit_after>package kickbox

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

type Client struct {
	apiKey string
	http   http.Client
}

func NewClient(apiKey string) Client {
	return Client{
		apiKey: apiKey,
		http:   http.Client{},
	}
}

// Configure the request timeout value (includes connecting, waiting for a response, and reading the response)
func (c Client) SetTimeout(time time.Duration) {
	c.http.Timeout = time
}

// Verify the given email address using Kickbox.io
func (c Client) Verify(address string) (*Result, error) {
	return c.verify(KickboxResultBuilder{}, c.url(address))
}

// Build the API endpoint given the email address and API key
func (c Client) url(address string) string {
	return fmt.Sprintf("https://api.kickbox.io/v2/verify?email=%s&apikey=%s", url.QueryEscape(address), url.QueryEscape(c.apiKey))
}

// Request and read the response of the HTTP request, returning a new Result struct
func (c Client) verify(rb ResultBuilder, url string) (*Result, error) {
	// Send our API request
	response, err := c.http.Get(url)
	if err != nil {
		return nil, err
	}

	// Did we get a HTTP 200?
	if response.StatusCode != 200 {
		msg := fmt.Sprintf("Kickbox API returned HTTP %d", response.Status)
		return nil, errors.New(msg)
	}

	// Read the response
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build our Result struct
	result, err := rb.NewResult(body)
	if err != nil {
		return nil, err
	}

	return result, nil
}
<|endoftext|>