<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"
)

const DefaultSection = "client"

const KeyLen = 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
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)-2]})
			login = &sections[len(sections)-1].Login
		} else if login != nil && line != "" {
			if err = login.parseLine(line); err != nil {
				return nil, err
			}
		}
	}
	return
}

type File interface {
	Key() [KeyLen]byte
	ByteOrder() binary.ByteOrder
	PlainText() io.Reader
}

type decoder struct {
	key       [KeyLen]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() [KeyLen]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 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 [KeyLen]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 < KeyLen; 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
}

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>Rename KeyLen to KeySize<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"
)

const DefaultSection = "client"

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
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)-2]})
			login = &sections[len(sections)-1].Login
		} else if login != nil && line != "" {
			if err = login.parseLine(line); err != nil {
				return nil, err
			}
		}
	}
	return
}

type File interface {
	Key() [KeySize]byte
	ByteOrder() binary.ByteOrder
	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 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
}

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 main

import (
	"database/sql"
	"io/ioutil"
	"regexp"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
)

var (
	errMySQLDuplicateEntry = uint16(0x426)
)

type mariaDB struct {
	db *sql.DB
}

func mustSetupMariaDB(config mariadbConfig) *mariaDB {
	db, err := setupMariaDB(config)
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("Could not setup MariaDB.")
	}
	return db
}

func setupMariaDB(conf mariadbConfig) (*mariaDB, error) {
	db, err := sql.Open("mysql", conf.URL)
	if err != nil {
		return nil, err
	}

	if err = db.Ping(); err != nil {
		return nil, err
	}

	log.Infof("Set up MariaDB connection for %#v.", conf.URL)

	mustSetupTables(db)

	log.Infof("Initialize schema and tables")

	return &mariaDB{db}, nil
}

func mustSetupTables(db *sql.DB) {
	createTableRegexp := regexp.MustCompile("(?s)(CREATE TABLE.+?;)")

	queries, err := ioutil.ReadFile("mariadb.sql")
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatalf("Failed to read cql initialization file.")
	}

	createQueries := createTableRegexp.FindAllString(string(queries), -1)
	for _, query := range createQueries {
		mustRunQuery(db, query)
	}
}

func mustRunQuery(db *sql.DB, sql string) {
	for _, q := range strings.Split(sql, ";") {
		if len(q) > 5 {
			_, err := db.Exec(q)
			if err != nil {
				log.WithFields(log.Fields{"query": q, "err": err}).Fatal("Failed to run sql in mariadb")
			}
		}
	}
}

func (m *mariaDB) saveScrape(topic string, partition int32, offset int64) error {
	q := `INSERT INTO bookie.scrape
					(topic, topic_partition, lastOffset, updated)
				VALUES
					(?, ?, ?, UTC_TIMESTAMP())
				ON DUPLICATE KEY UPDATE
					lastOffset = ?,
					updated = UTC_TIMESTAMP()
				`

	_, err := m.db.Exec(q,
		topic,
		partition,
		offset,
		offset,
	)

	if err != nil {
		fs := log.Fields{
			"tenant":     topic,
			"campaignID": partition,
			"execution":  offset,
			"err":        err,
		}
		log.WithFields(fs).Error("Failed to save scrape")
		return err
	}

	return nil
}

func (m *mariaDB) getLastNFSMs(n int) ([]fsm, error) {
	fsms := []fsm{}
	q := `SELECT
			f.fsmID, f.created, o.topic, o.topic_partition, o.startOffset, o.lastOffset, o.count
		  FROM
		  		(SELECT fsmID, created FROM bookie.fsm ORDER BY created DESC LIMIT ?) f
		  INNER JOIN
		  		offset o USING fsmID`

	dbRows, err := m.db.Query(q, n)
	if err != nil {
		return fsms, err
	}

	defer closeRows(dbRows)

	fsmMap := map[string]fsm{}
	for dbRows.Next() {
		var fsmID, topicValue string
		var partitionValue int32
		var startOffset, lastOffset, count int64
		var created time.Time

		if err = dbRows.Scan(&fsmID, &created, &topicValue, &partitionValue, &startOffset, &lastOffset, &count); err != nil {
			log.WithFields(log.Fields{"err": err, "number": n}).Errorf("failed to get last n fsms")
			return fsms, err
		}

		f, ok := fsmMap[fsmID]
		if !ok {
			f = fsm{
				ID:      fsmID,
				Created: created,
				Topics:  map[string]topic{},
			}
			fsmMap[fsmID] = f
		}

		t, ok := f.Topics[topicValue]
		if !ok {
			t = topic{
				Partitions: map[int32]partition{},
				Count:      0,
			}
			f.Topics[topicValue] = t
		}

		_, ok = t.Partitions[partitionValue]
		if !ok {
			p := partition{
				Start: startOffset,
				End:   lastOffset,
				Count: count,
			}
			t.Partitions[partitionValue] = p
		}
	}

	for _, f := range fsmMap {
		fsms = append(fsms, f)
	}

	return fsms, nil
}

func (m *mariaDB) saveFSM(f fsmDataPoint) error {
	q := `INSERT INTO bookie.fsm(fsmID, created) values (?, ?) ON DUPLICATE KEY UPDATE created = ?;`

	_, err := m.db.Exec(q,
		f.fsmID,
		f.created,
		f.created,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":   f.fsmID,
			"created": f.created,
			"err":     err,
		}
		log.WithFields(fs).Error("Failed to save FSM")
		return err
	}

	q = `INSERT INTO bookie.offset
				(fsmID, topic, topic_partition, startOffset, lastOffset, updated)
			VALUES
				(?, ?, ?, ?, ?, UTC_TIMESTAMP())
			ON DUPLICATE KEY UPDATE
				startOffset = ?,
				lastOffset = ?,
				updated = UTC_TIMESTAMP()
			`

	_, err = m.db.Exec(q,
		f.fsmID,
		f.topic,
		f.partition,
		f.startOffset,
		f.lastOffset,
		f.startOffset,
		f.lastOffset,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":       f.fsmID,
			"topic":       f.topic,
			"partition":   f.partition,
			"startOffset": f.startOffset,
			"lastOffset":  f.lastOffset,
			"err":         err,
		}
		log.WithFields(fs).Error("Failed to save FSM offsets")
		return err
	}

	return nil
}

func (m *mariaDB) saveAlias(fsmID string, fsmAlias string) error {
	q := `INSERT INTO bookie.fsmAliases(fsmID, fsmAlias) (?, ?) ON DUPLICATE KEY UPDATE 1 = 1`

	_, err := m.db.Exec(q,
		fsmID,
		fsmAlias,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":    fsmID,
			"fsmAlias": fsmAlias,
			"err":      err,
		}
		log.WithFields(fs).Error("Failed to save FSM alias")
		return err
	}

	return nil
}

func (m *mariaDB) mustLoadScrapes() map[string]topicRecord {
	trs := map[string]topicRecord{}

	q := `SELECT topic, topic_partition, lastOffset FROM bookie.scrape`
	dbRows, err := m.db.Query(q)
	if err != nil {
		log.Fatal(err)
	}

	for dbRows.Next() {
		var topic string
		var partition int32
		var lastOffset int64

		if err = dbRows.Scan(&topic, &partition, &lastOffset); err != nil {
			log.Fatal(err)
		}
		if _, ok := trs[topic]; !ok {
			trs[topic] = topicRecord{
				topic:      topic,
				partitions: map[int32]int64{},
			}
		}
		trs[topic].partitions[partition] = lastOffset
	}

	return trs
}

func (m *mariaDB) findFSM(fsmID string) ([]FSMRow, error) {
	var (
		err error
		fs  = log.Fields{"fsmID": fsmID}
	)
	rows := []FSMRow{}

	q := `SELECT fsmID, topic, partition, startOffset, lastOffset, updated FROM bookie.fsm WHERE fsmID = ?`
	dbRows, err := m.db.Query(q, fsmID)
	if err != nil {
		return rows, err
	}

	defer closeRows(dbRows)

	for dbRows.Next() {
		var fsmID, topic string
		var partition int32
		var startOffset, lastOffset int64
		var updated time.Time

		if err = dbRows.Scan(&fsmID, &topic, &partition, &startOffset, &lastOffset, &updated); err != nil {
			fs["error"] = err
			log.WithFields(fs).Errorf("failed to scan execution uuid")
			return rows, err
		}
		rows = append(rows, FSMRow{
			FSMID:       fsmID,
			Topic:       topic,
			Partition:   partition,
			StartOffset: startOffset,
			LastOffset:  lastOffset,
			Updated:     updated,
		})
	}

	return rows, nil
}

func closeRows(rows *sql.Rows) {
	if err := rows.Close(); err != nil {
		log.WithField("error", err).Error("failed to close rows")
	}
}
<commit_msg>executes queries other than CREATE TABLE.<commit_after>package main

import (
	"database/sql"
	"io/ioutil"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	_ "github.com/go-sql-driver/mysql"
)

var (
	errMySQLDuplicateEntry = uint16(0x426)
)

type mariaDB struct {
	db *sql.DB
}

func mustSetupMariaDB(config mariadbConfig) *mariaDB {
	db, err := setupMariaDB(config)
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("Could not setup MariaDB.")
	}
	return db
}

func setupMariaDB(conf mariadbConfig) (*mariaDB, error) {
	db, err := sql.Open("mysql", conf.URL)
	if err != nil {
		return nil, err
	}

	if err = db.Ping(); err != nil {
		return nil, err
	}

	log.Infof("Set up MariaDB connection for %#v.", conf.URL)

	mustRunMariaDBSQL(db)

	log.Infof("Initialize schema and tables")

	return &mariaDB{db}, nil
}

func mustRunMariaDBSQL(db *sql.DB) {
	queries, err := ioutil.ReadFile("mariadb.sql")
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatalf("Failed to read cql initialization file.")
	}

	mustRunQuery(db, string(queries))
}

func mustRunQuery(db *sql.DB, sql string) {
	for _, q := range strings.Split(sql, ";") {
		if len(q) > 5 {
			_, err := db.Exec(q)
			if err != nil {
				log.WithFields(log.Fields{"query": q, "err": err}).Fatal("Failed to run sql in mariadb")
			}
		}
	}
}

func (m *mariaDB) saveScrape(topic string, partition int32, offset int64) error {
	q := `INSERT INTO bookie.scrape
					(topic, topic_partition, lastOffset, updated)
				VALUES
					(?, ?, ?, UTC_TIMESTAMP())
				ON DUPLICATE KEY UPDATE
					lastOffset = ?,
					updated = UTC_TIMESTAMP()
				`

	_, err := m.db.Exec(q,
		topic,
		partition,
		offset,
		offset,
	)

	if err != nil {
		fs := log.Fields{
			"tenant":     topic,
			"campaignID": partition,
			"execution":  offset,
			"err":        err,
		}
		log.WithFields(fs).Error("Failed to save scrape")
		return err
	}

	return nil
}

func (m *mariaDB) getLastNFSMs(n int) ([]fsm, error) {
	fsms := []fsm{}
	q := `SELECT
			f.fsmID, f.created, o.topic, o.topic_partition, o.startOffset, o.lastOffset, o.count
		  FROM
		  		(SELECT fsmID, created FROM bookie.fsm ORDER BY created DESC LIMIT ?) f
		  INNER JOIN
		  		offset o USING fsmID`

	dbRows, err := m.db.Query(q, n)
	if err != nil {
		return fsms, err
	}

	defer closeRows(dbRows)

	fsmMap := map[string]fsm{}
	for dbRows.Next() {
		var fsmID, topicValue string
		var partitionValue int32
		var startOffset, lastOffset, count int64
		var created time.Time

		if err = dbRows.Scan(&fsmID, &created, &topicValue, &partitionValue, &startOffset, &lastOffset, &count); err != nil {
			log.WithFields(log.Fields{"err": err, "number": n}).Errorf("failed to get last n fsms")
			return fsms, err
		}

		f, ok := fsmMap[fsmID]
		if !ok {
			f = fsm{
				ID:      fsmID,
				Created: created,
				Topics:  map[string]topic{},
			}
			fsmMap[fsmID] = f
		}

		t, ok := f.Topics[topicValue]
		if !ok {
			t = topic{
				Partitions: map[int32]partition{},
				Count:      0,
			}
			f.Topics[topicValue] = t
		}

		_, ok = t.Partitions[partitionValue]
		if !ok {
			p := partition{
				Start: startOffset,
				End:   lastOffset,
				Count: count,
			}
			t.Partitions[partitionValue] = p
		}
	}

	for _, f := range fsmMap {
		fsms = append(fsms, f)
	}

	return fsms, nil
}

func (m *mariaDB) saveFSM(f fsmDataPoint) error {
	q := `INSERT INTO bookie.fsm(fsmID, created) values (?, ?) ON DUPLICATE KEY UPDATE created = ?;`

	_, err := m.db.Exec(q,
		f.fsmID,
		f.created,
		f.created,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":   f.fsmID,
			"created": f.created,
			"err":     err,
		}
		log.WithFields(fs).Error("Failed to save FSM")
		return err
	}

	q = `INSERT INTO bookie.offset
				(fsmID, topic, topic_partition, startOffset, lastOffset, updated)
			VALUES
				(?, ?, ?, ?, ?, UTC_TIMESTAMP())
			ON DUPLICATE KEY UPDATE
				startOffset = ?,
				lastOffset = ?,
				updated = UTC_TIMESTAMP()
			`

	_, err = m.db.Exec(q,
		f.fsmID,
		f.topic,
		f.partition,
		f.startOffset,
		f.lastOffset,
		f.startOffset,
		f.lastOffset,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":       f.fsmID,
			"topic":       f.topic,
			"partition":   f.partition,
			"startOffset": f.startOffset,
			"lastOffset":  f.lastOffset,
			"err":         err,
		}
		log.WithFields(fs).Error("Failed to save FSM offsets")
		return err
	}

	return nil
}

func (m *mariaDB) saveAlias(fsmID string, fsmAlias string) error {
	q := `INSERT INTO bookie.fsmAliases(fsmID, fsmAlias) (?, ?) ON DUPLICATE KEY UPDATE 1 = 1`

	_, err := m.db.Exec(q,
		fsmID,
		fsmAlias,
	)

	if err != nil {
		fs := log.Fields{
			"fsmID":    fsmID,
			"fsmAlias": fsmAlias,
			"err":      err,
		}
		log.WithFields(fs).Error("Failed to save FSM alias")
		return err
	}

	return nil
}

func (m *mariaDB) mustLoadScrapes() map[string]topicRecord {
	trs := map[string]topicRecord{}

	q := `SELECT topic, topic_partition, lastOffset FROM bookie.scrape`
	dbRows, err := m.db.Query(q)
	if err != nil {
		log.Fatal(err)
	}

	for dbRows.Next() {
		var topic string
		var partition int32
		var lastOffset int64

		if err = dbRows.Scan(&topic, &partition, &lastOffset); err != nil {
			log.Fatal(err)
		}
		if _, ok := trs[topic]; !ok {
			trs[topic] = topicRecord{
				topic:      topic,
				partitions: map[int32]int64{},
			}
		}
		trs[topic].partitions[partition] = lastOffset
	}

	return trs
}

func (m *mariaDB) findFSM(fsmID string) ([]FSMRow, error) {
	var (
		err error
		fs  = log.Fields{"fsmID": fsmID}
	)
	rows := []FSMRow{}

	q := `SELECT fsmID, topic, partition, startOffset, lastOffset, updated FROM bookie.fsm WHERE fsmID = ?`
	dbRows, err := m.db.Query(q, fsmID)
	if err != nil {
		return rows, err
	}

	defer closeRows(dbRows)

	for dbRows.Next() {
		var fsmID, topic string
		var partition int32
		var startOffset, lastOffset int64
		var updated time.Time

		if err = dbRows.Scan(&fsmID, &topic, &partition, &startOffset, &lastOffset, &updated); err != nil {
			fs["error"] = err
			log.WithFields(fs).Errorf("failed to scan execution uuid")
			return rows, err
		}
		rows = append(rows, FSMRow{
			FSMID:       fsmID,
			Topic:       topic,
			Partition:   partition,
			StartOffset: startOffset,
			LastOffset:  lastOffset,
			Updated:     updated,
		})
	}

	return rows, nil
}

func closeRows(rows *sql.Rows) {
	if err := rows.Close(); err != nil {
		log.WithField("error", err).Error("failed to close rows")
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/clearsign"
)

type PGPVerifyArg struct {
	Source       io.Reader
	Signature    []byte
	SignedBy     string
	TrackOptions keybase1.TrackOptions
}

// PGPVerify is an engine.
type PGPVerify struct {
	arg        *PGPVerifyArg
	peek       *libkb.Peeker
	signStatus *libkb.SignatureStatus
	owner      *libkb.User
	libkb.Contextified
}

// NewPGPVerify creates a PGPVerify engine.
func NewPGPVerify(arg *PGPVerifyArg, g *libkb.GlobalContext) *PGPVerify {
	return &PGPVerify{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PGPVerify) Name() string {
	return "PGPVerify"
}

// GetPrereqs returns the engine prereqs.
func (e *PGPVerify) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *PGPVerify) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PGPVerify) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&PGPDecrypt{},
		&ScanKeys{},
		&Identify{},
	}
}

// Run starts the engine.
func (e *PGPVerify) Run(ctx *Context) error {
	e.peek = libkb.NewPeeker(e.arg.Source)
	if libkb.IsClearsign(e.peek) {
		return e.runClearsign(ctx)
	}
	if len(e.arg.Signature) == 0 {
		return e.runAttached(ctx)
	}
	return e.runDetached(ctx)
}

func (e *PGPVerify) SignatureStatus() *libkb.SignatureStatus {
	return e.signStatus
}

func (e *PGPVerify) Owner() *libkb.User {
	return e.owner
}

// runAttached verifies an attached signature
func (e *PGPVerify) runAttached(ctx *Context) error {
	arg := &PGPDecryptArg{
		Source:       e.peek,
		Sink:         libkb.NopWriteCloser{W: ioutil.Discard},
		AssertSigned: true,
		TrackOptions: e.arg.TrackOptions,
	}
	eng := NewPGPDecrypt(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	e.signStatus = eng.SignatureStatus()
	e.owner = eng.Owner()

	if err := e.checkSignedBy(ctx); err != nil {
		return err
	}

	return nil
}

// runDetached verifies a detached signature
func (e *PGPVerify) runDetached(ctx *Context) error {
	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	checkfn := openpgp.CheckDetachedSignature
	if libkb.IsArmored(e.arg.Signature) {
		checkfn = openpgp.CheckArmoredDetachedSignature
	}
	signer, err := checkfn(sk, e.peek, bytes.NewReader(e.arg.Signature))
	if err != nil {
		return err
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

// runClearsign verifies a clearsign signature
func (e *PGPVerify) runClearsign(ctx *Context) error {
	// clearsign decode only works with the whole data slice, not a reader
	// so have to read it all here:
	msg, err := ioutil.ReadAll(e.peek)
	if err != nil {
		return err
	}
	b, _ := clearsign.Decode(msg)
	if b == nil {
		return errors.New("Unable to decode clearsigned message")
	}

	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	signer, err := openpgp.CheckDetachedSignature(sk, bytes.NewReader(b.Bytes), b.ArmoredSignature.Body)
	if err != nil {
		return fmt.Errorf("Check sig error: %s", err)
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

func (e *PGPVerify) checkSignedBy(ctx *Context) error {
	if len(e.arg.SignedBy) == 0 {
		// no assertion necessary
		return nil
	}
	if !e.signStatus.Verified || e.signStatus.Entity == nil || e.owner == nil {
		// signature not valid, so no need to assert
		return nil
	}

	// have: a valid signature, the signature's owner, and a user assertion to
	// match against
	e.G().Log.Debug("checking signed by assertion: %q", e.arg.SignedBy)

	// load the user in SignedBy
	arg := NewIdentifyArg(e.arg.SignedBy, false, false)
	eng := NewIdentify(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	signByUser := eng.User()
	if signByUser == nil {
		// this shouldn't happen (engine should return an error in this state)
		// but just in case:
		return libkb.ErrNilUser
	}

	// check if it is equal to signature owner
	if !e.owner.Equal(signByUser) {
		return libkb.BadSigError{
			E: fmt.Sprintf("Signer %q did not match signed by assertion %q", e.owner.GetName(), e.arg.SignedBy),
		}
	}

	return nil
}

func (e *PGPVerify) outputSuccess(ctx *Context, signer *openpgp.Entity, owner *libkb.User) {
	fingerprint := libkb.PGPFingerprint(signer.PrimaryKey.Fingerprint)
	ctx.LogUI.Notice("Signature verified. Signed by %s. PGP Fingerprint: %s", owner.GetName(), fingerprint)
}
<commit_msg>Get SignatureTime for detached signatures too<commit_after>package engine

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/armor"
	"golang.org/x/crypto/openpgp/clearsign"
	"golang.org/x/crypto/openpgp/packet"
)

type PGPVerifyArg struct {
	Source       io.Reader
	Signature    []byte
	SignedBy     string
	TrackOptions keybase1.TrackOptions
}

// PGPVerify is an engine.
type PGPVerify struct {
	arg        *PGPVerifyArg
	peek       *libkb.Peeker
	signStatus *libkb.SignatureStatus
	owner      *libkb.User
	libkb.Contextified
}

// NewPGPVerify creates a PGPVerify engine.
func NewPGPVerify(arg *PGPVerifyArg, g *libkb.GlobalContext) *PGPVerify {
	return &PGPVerify{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PGPVerify) Name() string {
	return "PGPVerify"
}

// GetPrereqs returns the engine prereqs.
func (e *PGPVerify) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *PGPVerify) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PGPVerify) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&PGPDecrypt{},
		&ScanKeys{},
		&Identify{},
	}
}

// Run starts the engine.
func (e *PGPVerify) Run(ctx *Context) error {
	e.peek = libkb.NewPeeker(e.arg.Source)
	if libkb.IsClearsign(e.peek) {
		return e.runClearsign(ctx)
	}
	if len(e.arg.Signature) == 0 {
		return e.runAttached(ctx)
	}
	return e.runDetached(ctx)
}

func (e *PGPVerify) SignatureStatus() *libkb.SignatureStatus {
	return e.signStatus
}

func (e *PGPVerify) Owner() *libkb.User {
	return e.owner
}

// runAttached verifies an attached signature
func (e *PGPVerify) runAttached(ctx *Context) error {
	arg := &PGPDecryptArg{
		Source:       e.peek,
		Sink:         libkb.NopWriteCloser{W: ioutil.Discard},
		AssertSigned: true,
		TrackOptions: e.arg.TrackOptions,
	}
	eng := NewPGPDecrypt(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	e.signStatus = eng.SignatureStatus()
	e.owner = eng.Owner()

	if err := e.checkSignedBy(ctx); err != nil {
		return err
	}

	return nil
}

// runDetached verifies a detached signature
func (e *PGPVerify) runDetached(ctx *Context) error {
	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	checkfn := openpgp.CheckDetachedSignature
	if libkb.IsArmored(e.arg.Signature) {
		checkfn = openpgp.CheckArmoredDetachedSignature
	}
	signer, err := checkfn(sk, e.peek, bytes.NewReader(e.arg.Signature))
	if err != nil {
		return err
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	b, err := armor.Decode(bytes.NewReader(e.arg.Signature))

	p, err := packet.Read(b.Body)

	if val, ok := p.(*packet.Signature); ok {
		e.signStatus.SignatureTime = val.CreationTime
	}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner(), e.signStatus.SignatureTime)
	}

	return nil
}

// runClearsign verifies a clearsign signature
func (e *PGPVerify) runClearsign(ctx *Context) error {
	// clearsign decode only works with the whole data slice, not a reader
	// so have to read it all here:
	msg, err := ioutil.ReadAll(e.peek)
	if err != nil {
		return err
	}
	b, _ := clearsign.Decode(msg)
	if b == nil {
		return errors.New("Unable to decode clearsigned message")
	}

	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	signer, err := openpgp.CheckDetachedSignature(sk, bytes.NewReader(b.Bytes), b.ArmoredSignature.Body)
	if err != nil {
		return fmt.Errorf("Check sig error: %s", err)
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner(), e.signStatus.SignatureTime)
	}

	return nil
}

func (e *PGPVerify) checkSignedBy(ctx *Context) error {
	if len(e.arg.SignedBy) == 0 {
		// no assertion necessary
		return nil
	}
	if !e.signStatus.Verified || e.signStatus.Entity == nil || e.owner == nil {
		// signature not valid, so no need to assert
		return nil
	}

	// have: a valid signature, the signature's owner, and a user assertion to
	// match against
	e.G().Log.Debug("checking signed by assertion: %q", e.arg.SignedBy)

	// load the user in SignedBy
	arg := NewIdentifyArg(e.arg.SignedBy, false, false)
	eng := NewIdentify(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	signByUser := eng.User()
	if signByUser == nil {
		// this shouldn't happen (engine should return an error in this state)
		// but just in case:
		return libkb.ErrNilUser
	}

	// check if it is equal to signature owner
	if !e.owner.Equal(signByUser) {
		return libkb.BadSigError{
			E: fmt.Sprintf("Signer %q did not match signed by assertion %q", e.owner.GetName(), e.arg.SignedBy),
		}
	}

	return nil
}

func (e *PGPVerify) outputSuccess(ctx *Context, signer *openpgp.Entity, owner *libkb.User, signatureTime time.Time) {
	fingerprint := libkb.PGPFingerprint(signer.PrimaryKey.Fingerprint)
	ctx.LogUI.Notice("Signature verified. Signed by %s %s (%s).", owner.GetName(), humanize.Time(e.signStatus.SignatureTime), e.signStatus.SignatureTime)
	ctx.LogUI.Notice("PGP Fingerprint: %s.", fingerprint)
}
<|endoftext|>


<commit_before>package vpnkit

import (
	"encoding/json"
	"io"

	"github.com/pkg/errors"
)

// DHCPConfiguration configures the built-in DHCP server.
type DHCPConfiguration struct {
	SearchDomains []string `json:"searchDomains"`
	DomainName    string   `json:"domainName"`
}

func (d DHCPConfiguration) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	if err := enc.Encode(d); err != nil {
		return errors.Wrap(err, "while writing DHCPConfiguration")
	}
	return nil
}

// HTTPConfiguration configures the built-in HTTP proxy.
type HTTPConfiguration struct {
	HTTP                  string   `json:"http,omitempty"`
	HTTPS                 string   `json:"https,omitempty"`
	Exclude               string   `json:"exclude,omitempty"`
	TransparentHTTPPorts  []int    `json:"transparent_http_ports"`
	TransparentHTTPSPorts []int    `json:"transparent_https_ports"`
	AllowEnabled          bool     `json:"allow_enabled"`
	Allow                 []string `json:"allow"`
	AllorErrorMsg         string   `json:"allow_error_msg"`
}

func (h HTTPConfiguration) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	if err := enc.Encode(h); err != nil {
		return errors.Wrap(err, "while writing HTTPConfiguration")
	}
	return nil
}

// Forward is a single forward from the gateway IP ExternalPort to (InternalIP, InternalPort)
type Forward struct {
	Protocol     Protocol `json:"protocol"`
	ExternalPort int      `json:"external_port"`
	InternalIP   string   `json:"internal_ip"`
	InternalPort int      `json:"internal_port"`
}

// GatewayForwards is a list of individual forwards.
type GatewayForwards []Forward

func (g GatewayForwards) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	if err := enc.Encode(g); err != nil {
		return errors.Wrap(err, "while writing GatewayForewards")
	}
	return nil
}
<commit_msg>go: fix typo in struct field name<commit_after>package vpnkit

import (
	"encoding/json"
	"io"

	"github.com/pkg/errors"
)

// DHCPConfiguration configures the built-in DHCP server.
type DHCPConfiguration struct {
	SearchDomains []string `json:"searchDomains"`
	DomainName    string   `json:"domainName"`
}

func (d DHCPConfiguration) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	if err := enc.Encode(d); err != nil {
		return errors.Wrap(err, "while writing DHCPConfiguration")
	}
	return nil
}

// HTTPConfiguration configures the built-in HTTP proxy.
type HTTPConfiguration struct {
	HTTP                  string   `json:"http,omitempty"`
	HTTPS                 string   `json:"https,omitempty"`
	Exclude               string   `json:"exclude,omitempty"`
	TransparentHTTPPorts  []int    `json:"transparent_http_ports"`
	TransparentHTTPSPorts []int    `json:"transparent_https_ports"`
	AllowEnabled          bool     `json:"allow_enabled"`
	Allow                 []string `json:"allow"`
	AllowErrorMsg         string   `json:"allow_error_msg"`
}

func (h HTTPConfiguration) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	if err := enc.Encode(h); err != nil {
		return errors.Wrap(err, "while writing HTTPConfiguration")
	}
	return nil
}

// Forward is a single forward from the gateway IP ExternalPort to (InternalIP, InternalPort)
type Forward struct {
	Protocol     Protocol `json:"protocol"`
	ExternalPort int      `json:"external_port"`
	InternalIP   string   `json:"internal_ip"`
	InternalPort int      `json:"internal_port"`
}

// GatewayForwards is a list of individual forwards.
type GatewayForwards []Forward

func (g GatewayForwards) Write(w io.Writer) error {
	enc := json.NewEncoder(w)
	if err := enc.Encode(g); err != nil {
		return errors.Wrap(err, "while writing GatewayForewards")
	}
	return nil
}
<|endoftext|>


<commit_before>package yext

import (
	"reflect"
)

type Comparable interface {
	Equal(Comparable) bool
}

// Diff calculates the differences between a base Location (a) and a proposed set of changes
// represented by a second Location (b).  The diffing logic will ignore fields in the proposed
// Location that aren't set (nil).  This characteristic makes the function ideal for
// calculting the minimal PUT required to bring an object "up-to-date" via the API.
//
// Example:
//   var (
//     // Typically, this would come from an authoritative source, like the API
//     base     = Location{Name: "Yext, Inc.", Address1: "123 Mulberry St"}
//     proposed = Location{Name: "Yext, Inc.", Address1: "456 Washington St"}
//   )
//
//   delta, isDiff := base.Diff(proposed)
//   // isDiff -> true
//   // delta -> &Location{Address1: "456 Washington St"}
//
//   delta, isDiff = base.Diff(base)
//   // isDiff -> false
//   // delta -> nil
func (y Location) Diff(b *Location) (d *Location, diff bool) {
	d = &Location{Id: String(y.GetId())}

	var (
		aV, bV = reflect.ValueOf(y), reflect.ValueOf(b).Elem()
		aT     = reflect.TypeOf(y)
		numA   = aV.NumField()
	)

	for i := 0; i < numA; i++ {
		var (
			nameA = aT.Field(i).Name
			valA  = aV.Field(i)
			valB  = bV.Field(i)
		)

		if !valB.CanSet() {
			continue
		}

		if valB.IsNil() || nameA == "Id" {
			continue
		}

		if nameA == "Hours" {
			if HoursAreEquivalent(getUnderlyingValue(valA).(string), getUnderlyingValue(valB).(string)) {
				continue
			}
		}

		if isZeroValue(valA, y.nilIsEmpty) && isZeroValue(valB, b.nilIsEmpty) {
			continue
		}

		aI, bI := valA.Interface(), valB.Interface()

		comparableA, aOk := aI.(Comparable)
		comparableB, bOk := bI.(Comparable)

		if aOk && bOk {
			if !comparableA.Equal(comparableB) {
				diff = true
				reflect.ValueOf(d).Elem().FieldByName(nameA).Set(valB)
			}
		} else if !reflect.DeepEqual(aI, bI) {
			if nameA == "CustomFields" {
				d.CustomFields = make(map[string]interface{})
				for field, value := range b.CustomFields {
					value = getUnderlyingValue(value)
					if aValue, ok := y.CustomFields[field]; ok {
						aValue = getUnderlyingValue(aValue)
						var valueDiff bool
						if v, ok := aValue.(Comparable); ok {
							valueDiff = !v.Equal(value.(Comparable))
						} else if !reflect.DeepEqual(aValue, value) {
							valueDiff = true
						}
						if valueDiff {
							diff = true
							d.CustomFields[field] = value
						}
					} else {
						d.CustomFields[field] = value
						diff = true
					}
				}
			} else {
				diff = true
				reflect.ValueOf(d).Elem().FieldByName(nameA).Set(valB)
			}
		}
	}
	if diff == false {
		// ensure d remains nil if nothing has changed
		d = nil
	}

	return d, diff
}

// getUnderlyingValue unwraps a pointer/interface to get the underlying value.
// If the value is already unwrapped, it returns it as is.
func getUnderlyingValue(v interface{}) interface{} {
	rv := reflect.ValueOf(v)

	switch rv.Kind() {
	case reflect.Ptr, reflect.Interface:
		rv = rv.Elem()
	}

	return rv.Interface()
}

func isZeroValue(v reflect.Value, interpretNilAsZeroValue bool) bool {
	if !v.IsValid() {
		return true
	}

	switch v.Kind() {
	case reflect.Slice, reflect.Map:
		return v.Len() == 0
	case reflect.Bool:
		return v.Bool() == false
	case reflect.String:
		return v.String() == ""
	case reflect.Float64:
		return v.Float() == 0.0
	case reflect.Ptr, reflect.Interface:
		if v.IsNil() && !interpretNilAsZeroValue {
			return false
		}
		return isZeroValue(v.Elem(), interpretNilAsZeroValue)
	case reflect.Struct:
		for i, n := 0, v.NumField(); i < n; i++ {
			if !isZeroValue(v.Field(i), interpretNilAsZeroValue) {
				return false
			}
		}
		return true
	default:
		return v.IsNil() && interpretNilAsZeroValue
	}
}

var closedHoursEquivalents = map[string]struct{}{
	"": struct{}{},
	"1:closed,2:closed,3:closed,4:closed,5:closed,6:closed,7:closed": struct{}{},
}

func HoursAreEquivalent(a, b string) bool {
	_, aIsClosed := closedHoursEquivalents[a]
	_, bIsClosed := closedHoursEquivalents[b]

	return a == b || aIsClosed && bIsClosed
}
<commit_msg>location_diff.go: update hours to reflect reversion of getUnderlyingValue<commit_after>package yext

import (
	"reflect"
)

type Comparable interface {
	Equal(Comparable) bool
}

// Diff calculates the differences between a base Location (a) and a proposed set of changes
// represented by a second Location (b).  The diffing logic will ignore fields in the proposed
// Location that aren't set (nil).  This characteristic makes the function ideal for
// calculting the minimal PUT required to bring an object "up-to-date" via the API.
//
// Example:
//   var (
//     // Typically, this would come from an authoritative source, like the API
//     base     = Location{Name: "Yext, Inc.", Address1: "123 Mulberry St"}
//     proposed = Location{Name: "Yext, Inc.", Address1: "456 Washington St"}
//   )
//
//   delta, isDiff := base.Diff(proposed)
//   // isDiff -> true
//   // delta -> &Location{Address1: "456 Washington St"}
//
//   delta, isDiff = base.Diff(base)
//   // isDiff -> false
//   // delta -> nil
func (y Location) Diff(b *Location) (d *Location, diff bool) {
	d = &Location{Id: String(y.GetId())}

	var (
		aV, bV = reflect.ValueOf(y), reflect.ValueOf(b).Elem()
		aT     = reflect.TypeOf(y)
		numA   = aV.NumField()
	)

	for i := 0; i < numA; i++ {
		var (
			nameA = aT.Field(i).Name
			valA  = aV.Field(i)
			valB  = bV.Field(i)
		)

		if !valB.CanSet() {
			continue
		}

		if valB.IsNil() || nameA == "Id" {
			continue
		}

		if nameA == "Hours" {
			if HoursAreEquivalent(getUnderlyingValue(valA.Interface()).(string), getUnderlyingValue(valB.Interface()).(string)) {
				continue
			}
		}

		if isZeroValue(valA, y.nilIsEmpty) && isZeroValue(valB, b.nilIsEmpty) {
			continue
		}

		aI, bI := valA.Interface(), valB.Interface()

		comparableA, aOk := aI.(Comparable)
		comparableB, bOk := bI.(Comparable)

		if aOk && bOk {
			if !comparableA.Equal(comparableB) {
				diff = true
				reflect.ValueOf(d).Elem().FieldByName(nameA).Set(valB)
			}
		} else if !reflect.DeepEqual(aI, bI) {
			if nameA == "CustomFields" {
				d.CustomFields = make(map[string]interface{})
				for field, value := range b.CustomFields {
					value = getUnderlyingValue(value)
					if aValue, ok := y.CustomFields[field]; ok {
						aValue = getUnderlyingValue(aValue)
						var valueDiff bool
						if v, ok := aValue.(Comparable); ok {
							valueDiff = !v.Equal(value.(Comparable))
						} else if !reflect.DeepEqual(aValue, value) {
							valueDiff = true
						}
						if valueDiff {
							diff = true
							d.CustomFields[field] = value
						}
					} else {
						d.CustomFields[field] = value
						diff = true
					}
				}
			} else {
				diff = true
				reflect.ValueOf(d).Elem().FieldByName(nameA).Set(valB)
			}
		}
	}
	if diff == false {
		// ensure d remains nil if nothing has changed
		d = nil
	}

	return d, diff
}

// getUnderlyingValue unwraps a pointer/interface to get the underlying value.
// If the value is already unwrapped, it returns it as is.
func getUnderlyingValue(v interface{}) interface{} {
	rv := reflect.ValueOf(v)

	switch rv.Kind() {
	case reflect.Ptr, reflect.Interface:
		rv = rv.Elem()
	}

	return rv.Interface()
}

func isZeroValue(v reflect.Value, interpretNilAsZeroValue bool) bool {
	if !v.IsValid() {
		return true
	}

	switch v.Kind() {
	case reflect.Slice, reflect.Map:
		return v.Len() == 0
	case reflect.Bool:
		return v.Bool() == false
	case reflect.String:
		return v.String() == ""
	case reflect.Float64:
		return v.Float() == 0.0
	case reflect.Ptr, reflect.Interface:
		if v.IsNil() && !interpretNilAsZeroValue {
			return false
		}
		return isZeroValue(v.Elem(), interpretNilAsZeroValue)
	case reflect.Struct:
		for i, n := 0, v.NumField(); i < n; i++ {
			if !isZeroValue(v.Field(i), interpretNilAsZeroValue) {
				return false
			}
		}
		return true
	default:
		return v.IsNil() && interpretNilAsZeroValue
	}
}

var closedHoursEquivalents = map[string]struct{}{
	"": struct{}{},
	"1:closed,2:closed,3:closed,4:closed,5:closed,6:closed,7:closed": struct{}{},
}

func HoursAreEquivalent(a, b string) bool {
	_, aIsClosed := closedHoursEquivalents[a]
	_, bIsClosed := closedHoursEquivalents[b]

	return a == b || aIsClosed && bIsClosed
}
<|endoftext|>


<commit_before>package locking

import (
	"strings"

	"github.com/git-lfs/git-lfs/tools/kv"
)

const (
	// We want to use a single cache file for integrity, but to make it easy to
	// list all locks, prefix the id->path map in a way we can identify (something
	// that won't be in a path)
	idKeyPrefix string = "*id*://"
)

type LockCache struct {
	kv *kv.Store
}

func NewLockCache(filepath string) (*LockCache, error) {
	kv, err := kv.NewStore(filepath)
	if err != nil {
		return nil, err
	}
	return &LockCache{kv}, nil
}

func (c *LockCache) encodeIdKey(id string) string {
	// Safety against accidents
	if !c.isIdKey(id) {
		return idKeyPrefix + id
	}
	return id
}
func (c *LockCache) decodeIdKey(key string) string {
	// Safety against accidents
	if c.isIdKey(key) {
		return key[len(idKeyPrefix):]
	}
	return key
}
func (c *LockCache) isIdKey(key string) bool {
	return strings.HasPrefix(key, idKeyPrefix)
}

// Cache a successful lock for faster local lookup later
func (c *LockCache) CacheLock(l Lock) error {
	// Store reference in both directions
	// Path -> Lock
	c.kv.Set(l.Path, &l)
	// EncodedId -> Lock (encoded so we can easily identify)
	c.kv.Set(c.encodeIdKey(l.Id), &l)
	return nil
}

// Remove a cached lock by path becuase it's been relinquished
func (c *LockCache) CacheUnlockByPath(filePath string) error {
	ilock := c.kv.Get(filePath)
	if ilock != nil {
		lock := ilock.(*Lock)
		c.kv.Remove(lock.Path)
		// Id as key is encoded
		c.kv.Remove(c.encodeIdKey(lock.Id))
	}
	return nil
}

// Remove a cached lock by id because it's been relinquished
func (c *LockCache) CacheUnlockById(id string) error {
	// Id as key is encoded
	idkey := c.encodeIdKey(id)
	ilock := c.kv.Get(idkey)
	if ilock != nil {
		lock := ilock.(*Lock)
		c.kv.Remove(idkey)
		c.kv.Remove(lock.Path)
	}
	return nil
}

// Get the list of cached locked files
func (c *LockCache) CachedLocks() []Lock {
	var locks []Lock
	c.kv.Visit(func(key string, val interface{}) bool {
		// Only report file->id entries not reverse
		if !c.isIdKey(key) {
			lock := val.(*Lock)
			locks = append(locks, *lock)
		}
		return true // continue
	})
	return locks
}

// Clear the cache
func (c *LockCache) Clear() {
	c.kv.RemoveAll()
}

// Save the cache
func (c *LockCache) Save() error {
	return c.kv.Save()
}
<commit_msg>Protect against failed casts<commit_after>package locking

import (
	"strings"

	"github.com/git-lfs/git-lfs/tools/kv"
)

const (
	// We want to use a single cache file for integrity, but to make it easy to
	// list all locks, prefix the id->path map in a way we can identify (something
	// that won't be in a path)
	idKeyPrefix string = "*id*://"
)

type LockCache struct {
	kv *kv.Store
}

func NewLockCache(filepath string) (*LockCache, error) {
	kv, err := kv.NewStore(filepath)
	if err != nil {
		return nil, err
	}
	return &LockCache{kv}, nil
}

func (c *LockCache) encodeIdKey(id string) string {
	// Safety against accidents
	if !c.isIdKey(id) {
		return idKeyPrefix + id
	}
	return id
}
func (c *LockCache) decodeIdKey(key string) string {
	// Safety against accidents
	if c.isIdKey(key) {
		return key[len(idKeyPrefix):]
	}
	return key
}
func (c *LockCache) isIdKey(key string) bool {
	return strings.HasPrefix(key, idKeyPrefix)
}

// Cache a successful lock for faster local lookup later
func (c *LockCache) CacheLock(l Lock) error {
	// Store reference in both directions
	// Path -> Lock
	c.kv.Set(l.Path, &l)
	// EncodedId -> Lock (encoded so we can easily identify)
	c.kv.Set(c.encodeIdKey(l.Id), &l)
	return nil
}

// Remove a cached lock by path becuase it's been relinquished
func (c *LockCache) CacheUnlockByPath(filePath string) error {
	ilock := c.kv.Get(filePath)
	if lock, ok := ilock.(*Lock); ok && lock != nil {
		c.kv.Remove(lock.Path)
		// Id as key is encoded
		c.kv.Remove(c.encodeIdKey(lock.Id))
	}
	return nil
}

// Remove a cached lock by id because it's been relinquished
func (c *LockCache) CacheUnlockById(id string) error {
	// Id as key is encoded
	idkey := c.encodeIdKey(id)
	ilock := c.kv.Get(idkey)
	if lock, ok := ilock.(*Lock); ok && lock != nil {
		c.kv.Remove(idkey)
		c.kv.Remove(lock.Path)
	}
	return nil
}

// Get the list of cached locked files
func (c *LockCache) CachedLocks() []Lock {
	var locks []Lock
	c.kv.Visit(func(key string, val interface{}) bool {
		// Only report file->id entries not reverse
		if !c.isIdKey(key) {
			lock := val.(*Lock)
			locks = append(locks, *lock)
		}
		return true // continue
	})
	return locks
}

// Clear the cache
func (c *LockCache) Clear() {
	c.kv.RemoveAll()
}

// Save the cache
func (c *LockCache) Save() error {
	return c.kv.Save()
}
<|endoftext|>


<commit_before>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
}

type TaskList []Task

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)
        id := 0

        for scanner.Scan() {
                var task = Task{}
                text := scanner.Text()
                task.id = id
                task.raw_todo = text

                splits := strings.Split(text, " ")

                if text[0] == 'x' &&
                   text[1] == ' ' &&
                   !unicode.IsSpace(rune(text[2])) {
                        task.finished = true
                        splits = splits[1:]
                }

                head := splits[0]

                if (len(head) == 3) &&
                   (head[0] == '(') &&
                   (head[2] == ')') &&
                   (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                        task.priority = head[1]
                        splits = splits[1:]
                }

                date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"
                if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                        if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                                panic(e)
                        } else {
                                task.create_date = date
                        }

                        task.todo = strings.Join(splits[1:], " ")
                } else {
                        task.todo = strings.Join(splits[0:], " ")
                }

                context_regexp, _ := regexp.Compile("@[[:word:]]+")
                contexts := context_regexp.FindAllStringSubmatch(text, -1)
                if len(contexts) != 0 {
                        task.contexts = contexts[0]
                }

                project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
                projects := project_regexp.FindAllStringSubmatch(text, -1)
                if len(projects) != 0 {
                        task.projects = projects[0]
                }

                tasklist = append(tasklist, task)
                id += 1
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}
<commit_msg>started working on PrettyPrint<commit_after>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
}

type TaskList []Task

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)
        id := 0

        for scanner.Scan() {
                var task = Task{}
                text := scanner.Text()
                task.id = id
                task.raw_todo = text

                splits := strings.Split(text, " ")

                if text[0] == 'x' &&
                   text[1] == ' ' &&
                   !unicode.IsSpace(rune(text[2])) {
                        task.finished = true
                        splits = splits[1:]
                }

                head := splits[0]

                if (len(head) == 3) &&
                   (head[0] == '(') &&
                   (head[2] == ')') &&
                   (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                        task.priority = head[1]
                        splits = splits[1:]
                }

                date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"
                if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                        if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                                panic(e)
                        } else {
                                task.create_date = date
                        }

                        task.todo = strings.Join(splits[1:], " ")
                } else {
                        task.todo = strings.Join(splits[0:], " ")
                }

                context_regexp, _ := regexp.Compile("@[[:word:]]+")
                contexts := context_regexp.FindAllStringSubmatch(text, -1)
                if len(contexts) != 0 {
                        task.contexts = contexts[0]
                }

                project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
                projects := project_regexp.FindAllStringSubmatch(text, -1)
                if len(projects) != 0 {
                        task.projects = projects[0]
                }

                tasklist = append(tasklist, task)
                id += 1
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%[a-zA-Z])")
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {
                switch s{
                case "%t":
                        return task.Text()
                case "%p":
                        return string(task.Priority())
                default:
                        return s
                }
        })
        return out
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Package logger allows logging HTTP requests using customized formats.
package logger

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/c4milo/handlers/internal"
	"github.com/satori/go.uuid"
)

// http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
type Option func(*handler)

// Internal handler
type handler struct {
	name   string
	format string
	flags  int
	out    io.Writer
}

// AppName allows to set the application name to log.
func AppName(name string) Option {
	return func(l *handler) {
		l.name = name
	}
}

// Format allows to set a custom log format. Although, the timestamp is always logged at the beginning.
// This handler is a bit opinionated.
//
// Directives:
//
// {remote_user}		: Remote user if Basic Auth credentials were sent
// {remote_ip}			: Remote IP address.
// {latency}			: The time taken to serve the request, in microseconds.
// {latency_human}		: The time taken to serve the request, human readable.
// {id}					: The request ID.
// {host}				: The Host header sent to the server
// {method}				: The request method. Ex: GET, POST, DELETE, etc.
// {url}				: The URL path requested.
// {query}				: Request's query string
// {rxbytes}			: Bytes received without headers
// {txbytes}			: Bytes sent, excluding HTTP headers.
// {status}				: Status sent to the client
// {useragent}			: User Agent
// {referer}			: The site from where the request came from
//
func Format(format string) Option {
	return func(l *handler) {
		l.format = format
	}
}

// Flags allows to set logging flags using Go's standard log flags.
//
// Example: log.LstdFlags | log.shortfile
// Keep in mind that log.shortfile and log.Llongfile are expensive flags
func Flags(flags int) Option {
	return func(l *handler) {
		l.flags = flags
	}
}

// Output allows setting an output writer for logging to be written to
func Output(out io.Writer) Option {
	return func(l *handler) {
		l.out = out
	}
}

// Handler does HTTP request logging
func Handler(h http.Handler, opts ...Option) http.Handler {
	// Default options
	handler := &handler{
		name:   "unknown_app",
		format: `{id} remote_ip={remote_ip} {method} "{host}{url}?{query}" status={status} latency_human={latency_human} latency={latency} rxbytes={rxbytes} txbytes={txbytes}`,
		out:    os.Stdout,
		flags:  log.LstdFlags | log.Lmicroseconds,
	}

	for _, opt := range opts {
		opt(handler)
	}

	l := log.New(handler.out, fmt.Sprintf("[%s] ", handler.name), handler.flags)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// If there is a request ID already, we use it to keep the transaction
		// traceable. If not, we generate a new request ID.
		reqID := w.Header().Get("Request-ID")
		if reqID == "" {
			reqID = uuid.NewV4().String()
		}

		w.Header().Set("Request-ID", reqID)

		l.Print(applyLogFormat(handler.format, -1, w, r))

		res := internal.NewResponseWriter(w)
		h.ServeHTTP(res, r)

		latency := time.Since(start)
		l.Print(applyLogFormat(handler.format, latency, res, r))
	})
}

func userIPFromRequest(req *http.Request) string {
	ip, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		return ""
	}
	return ip
}

func applyLogFormat(format string, latency time.Duration, w http.ResponseWriter, r *http.Request) string {
	reqID := w.Header().Get("Request-ID")

	if strings.Index(format, "{remote_ip}") > -1 {
		format = strings.Replace(format, "{remote_ip}", userIPFromRequest(r), -1)
	}

	if strings.Index(format, "{remote_user}") > -1 {
		user, _, _ := r.BasicAuth()
		if user == "" {
			user = r.URL.User.Username()
		}
		format = strings.Replace(format, "{remote_user}", user, -1)
	}

	if strings.Index(format, "{latency_human}") > -1 {
		l := "..."
		if latency > -1 {
			l = latency.String()
		}
		format = strings.Replace(format, "{latency_human}", l, -1)
	}

	if strings.Index(format, "{latency}") > -1 {
		l := "..."
		if latency > -1 {
			l = strconv.FormatInt(latency.Nanoseconds(), 10)
		}
		format = strings.Replace(format, "{latency}", l, -1)
	}

	if strings.Index(format, "{id}") > -1 {
		format = strings.Replace(format, "{id}", reqID, -1)
	}

	if strings.Index(format, "{method}") > -1 {
		format = strings.Replace(format, "{method}", r.Method, -1)
	}

	if strings.Index(format, "{url}") > -1 {
		format = strings.Replace(format, "{url}", r.URL.Path, -1)
	}

	if strings.Index(format, "{query}") > -1 {
		format = strings.Replace(format, "{query}", r.URL.RawQuery, -1)
	}

	if strings.Index(format, "{rxbytes}") > -1 {
		format = strings.Replace(format, "{rxbytes}", strconv.FormatInt(r.ContentLength, 10), -1)
	}

	if strings.Index(format, "{txbytes}") > -1 {
		size := "..."
		if v, ok := w.(internal.ResponseWriter); ok {
			size = strconv.Itoa(v.Size())
		}
		format = strings.Replace(format, "{txbytes}", size, -1)
	}

	if strings.Index(format, "{status}") > -1 {
		status := "..."
		if v, ok := w.(internal.ResponseWriter); ok {
			status = strconv.Itoa(v.Status())
		}
		format = strings.Replace(format, "{status}", status, -1)
	}

	if strings.Index(format, "{useragent}") > -1 {
		format = strings.Replace(format, "{useragent}", r.UserAgent(), -1)
	}

	if strings.Index(format, "{host}") > -1 {
		format = strings.Replace(format, "{host}", r.Host, -1)
	}

	if strings.Index(format, "{referer}") > -1 {
		format = strings.Replace(format, "{referer}", r.Referer(), -1)
	}

	return format
}
<commit_msg>Adds support for logging URL scheme<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Package logger allows logging HTTP requests using customized formats.
package logger

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/c4milo/handlers/internal"
	"github.com/satori/go.uuid"
)

// Option implements http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html
type Option func(*handler)

// Internal handler
type handler struct {
	name   string
	format string
	flags  int
	out    io.Writer
}

// AppName allows to set the application name to log.
func AppName(name string) Option {
	return func(l *handler) {
		l.name = name
	}
}

// Format allows to set a custom log format. Although, the timestamp is always logged at the beginning.
// This handler is a bit opinionated.
//
// Directives:
//
// {remote_user}		: Remote user if Basic Auth credentials were sent
// {remote_ip}			: Remote IP address.
// {latency}			: The time taken to serve the request, in microseconds.
// {latency_human}		: The time taken to serve the request, human readable.
// {id}					: The request ID.
// {host}				: The Host header sent to the server
// {scheme}             : The protocol scheme used, either http or https.
// {method}				: The request method. Ex: GET, POST, DELETE, etc.
// {url}				: The URL path requested.
// {query}				: Request's query string
// {rxbytes}			: Bytes received without headers
// {txbytes}			: Bytes sent, excluding HTTP headers.
// {status}				: Status sent to the client
// {useragent}			: User Agent
// {referer}			: The site from where the request came from
//
func Format(format string) Option {
	return func(l *handler) {
		l.format = format
	}
}

// Flags allows to set logging flags using Go's standard log flags.
//
// Example: log.LstdFlags | log.shortfile
// Keep in mind that log.shortfile and log.Llongfile are expensive flags
func Flags(flags int) Option {
	return func(l *handler) {
		l.flags = flags
	}
}

// Output allows setting an output writer for logging to be written to
func Output(out io.Writer) Option {
	return func(l *handler) {
		l.out = out
	}
}

// Handler does HTTP request logging
func Handler(h http.Handler, opts ...Option) http.Handler {
	// Default options
	handler := &handler{
		name:   "unknown_app",
		format: `{id} remote_ip={remote_ip} {method} "{scheme}{host}{url}?{query}" status={status} latency_human={latency_human} latency={latency} rxbytes={rxbytes} txbytes={txbytes}`,
		out:    os.Stdout,
		flags:  log.LstdFlags | log.Lmicroseconds,
	}

	for _, opt := range opts {
		opt(handler)
	}

	l := log.New(handler.out, fmt.Sprintf("[%s] ", handler.name), handler.flags)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// If there is a request ID already, we use it to keep the transaction
		// traceable. If not, we generate a new request ID.
		reqID := w.Header().Get("Request-ID")
		if reqID == "" {
			reqID = uuid.NewV4().String()
		}

		w.Header().Set("Request-ID", reqID)

		l.Print(applyLogFormat(handler.format, -1, w, r))

		res := internal.NewResponseWriter(w)
		h.ServeHTTP(res, r)

		latency := time.Since(start)
		l.Print(applyLogFormat(handler.format, latency, res, r))
	})
}

func userIP(req *http.Request) string {
	ip, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		return ""
	}
	return ip
}

func urlScheme(req *http.Request) string {
	if req.TLS != nil {
		return "https://"
	}
	return "http://"
}

func remoteUser(req *http.Request) string {
	user, _, _ := req.BasicAuth()
	if user == "" && req.URL.User != nil {
		user = req.URL.User.Username()
	}

	return user
}

func applyLogFormat(format string, latency time.Duration, w http.ResponseWriter, r *http.Request) string {
	reqID := w.Header().Get("Request-ID")

	values := map[string]string{
		"{remote_ip}":   userIP(r),
		"{remote_user}": remoteUser(r),
		"{id}":          reqID,
		"{method}":      r.Method,
		"{url}":         r.URL.Path,
		"{query}":       r.URL.RawQuery,
		"{rxbytes}":     strconv.FormatInt(r.ContentLength, 10),
		"{useragent}":   r.UserAgent(),
		"{host}":        r.Host,
		"{referer}":     r.Referer(),
		"{scheme}":      urlScheme(r),
	}

	for k, v := range values {
		if strings.Contains(format, k) {
			format = strings.Replace(format, k, v, -1)
		}
	}

	if strings.Contains(format, "{latency_human}") {
		l := "..."
		if latency > -1 {
			l = latency.String()
		}
		format = strings.Replace(format, "{latency_human}", l, -1)
	}

	if strings.Contains(format, "{latency}") {
		l := "..."
		if latency > -1 {
			l = strconv.FormatInt(latency.Nanoseconds(), 10)
		}
		format = strings.Replace(format, "{latency}", l, -1)
	}

	if strings.Contains(format, "{txbytes}") {
		size := "..."
		if v, ok := w.(internal.ResponseWriter); ok {
			size = strconv.Itoa(v.Size())
		}
		format = strings.Replace(format, "{txbytes}", size, -1)
	}

	if strings.Contains(format, "{status}") {
		status := "..."
		if v, ok := w.(internal.ResponseWriter); ok {
			status = strconv.Itoa(v.Status())
		}
		format = strings.Replace(format, "{status}", status, -1)
	}

	return format
}
<|endoftext|>


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

// This file encapsulates some of the odd characteristics of the
// AMD64 instruction set, to minimize its interaction
// with the core of the assembler.

package arch

import (
	"cmd/internal/obj"
	"cmd/internal/obj/x86"
)

// IsAMD4OP reports whether the op (as defined by an ppc64.A* constant) is
// The FMADD-like instructions behave similarly.
func IsAMD4OP(op obj.As) bool {
	switch op {
	case x86.AVPERM2F128,
		x86.AVPALIGNR,
		x86.AVPERM2I128,
		x86.AVINSERTI128,
		x86.AVPBLENDD:
		return true
	}
	return false
}
<commit_msg>cmd/asm/internal/asm: fix copy/paste errors in comment<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file encapsulates some of the odd characteristics of the
// AMD64 instruction set, to minimize its interaction
// with the core of the assembler.

package arch

import (
	"cmd/internal/obj"
	"cmd/internal/obj/x86"
)

// IsAMD4OP reports whether the op (as defined by an amd64.A* constant) is
// a 4-operand instruction.
func IsAMD4OP(op obj.As) bool {
	switch op {
	case x86.AVPERM2F128,
		x86.AVPALIGNR,
		x86.AVPERM2I128,
		x86.AVINSERTI128,
		x86.AVPBLENDD:
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/getgauge/gauge/plugin/pluginInfo"
	"github.com/getgauge/gauge/version"

	"runtime"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/natefinch/lumberjack"
	"github.com/op/go-logging"
)

// Channel specifies the logging channel. Can be one of CLI, API or LSP
type channel int

const (
	logsDirectory    = "logs_directory"
	logs             = "logs"
	gaugeLogFileName = "gauge.log"
	apiLogFileName   = "api.log"
	LspLogFileName   = "lsp.log"
	// CLI indicates gauge is used as a CLI.
	CLI channel = iota
	// API indicates gauge is in daemon mode. Used in IDEs.
	API
	// LSP indicates that gauge is acting as an LSP server.
	LSP
)

var level logging.Level
var activeLogger *logging.Logger
var fileLogFormat = logging.MustStringFormatter("%{time:02-01-2006 15:04:05.000} [Gauge] [%{level}] %{message}")
var isLSP bool
var initialized bool
var ActiveLogFile string
var machineReadable bool

// Info logs INFO messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Info(stdout bool, msg string) {
	Infof(stdout, msg)
}

// Infof logs INFO messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Infof(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		return
	}
	activeLogger.Infof(msg, args...)
}

// Error logs ERROR messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Error(stdout bool, msg string) {
	Errorf(stdout, msg)
}

// Errorf logs ERROR messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Errorf(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		fmt.Fprintf(os.Stderr, msg, args...)
		return
	}
	activeLogger.Errorf(msg, args...)
}

// Warning logs WARNING messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Warning(stdout bool, msg string) {
	Warningf(stdout, msg)
}

// Warningf logs WARNING messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Warningf(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		return
	}
	activeLogger.Warningf(msg, args...)
}

// Fatal logs CRITICAL messages and exits. stdout flag indicates if message is to be written to stdout in addition to log.
func Fatal(stdout bool, msg string) {
	Fatalf(stdout, msg)
}

// Fatalf logs CRITICAL messages and exits. stdout flag indicates if message is to be written to stdout in addition to log.
func Fatalf(stdout bool, msg string, args ...interface{}) {
	message := getErrorText(msg, args...)
	if !initialized {
		fmt.Fprintf(os.Stderr, msg, args...)
		return
	}
	write(stdout, message)
	activeLogger.Fatalf(msg, args...)
}

// Debug logs DEBUG messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Debug(stdout bool, msg string) {
	Debugf(stdout, msg)
}

// Debugf logs DEBUG messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Debugf(stdout bool, msg string, args ...interface{}) {
	if !initialized {
		return
	}
	activeLogger.Debugf(msg, args...)
	if level == logging.DEBUG {
		write(stdout, msg, args...)
	}
}

func getErrorText(msg string, args ...interface{}) string {
	env := []string{runtime.GOOS, version.FullVersion()}
	if version.GetCommitHash() != "" {
		env = append(env, version.GetCommitHash())
	}
	envText := strings.Join(env, ", ")
	return fmt.Sprintf(`Error ----------------------------------

%s

Get Support ----------------------------
	Docs:          https://docs.gauge.org
	Bugs:          https://github.com/getgauge/gauge/issues
	Chat:          https://gitter.im/getgauge/chat

Your Environment Information -----------
	%s
	%s`, fmt.Sprintf(msg, args...),
		envText,
		getPluginVersions())
}

func getPluginVersions() string {
	pis, err := pluginInfo.GetAllInstalledPluginsWithVersion()
	if err != nil {
		return fmt.Sprintf("Could not retrieve plugin information.")
	}
	pluginVersions := make([]string, 0, 0)
	for _, pi := range pis {
		pluginVersions = append(pluginVersions, fmt.Sprintf(`%s (%s)`, pi.Name, pi.Version))
	}
	return strings.Join(pluginVersions, ", ")
}

type out struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func (out *out) toJSON() (string, error) {
	json, err := json.Marshal(out)
	if err != nil {
		return "", err
	}
	return string(json), nil
}
func write(stdout bool, msg string, args ...interface{}) {
	if !isLSP && stdout {
		if machineReadable {
			strs := strings.Split(fmt.Sprintf(msg, args...), "\n")
			for _, m := range strs {
				outMessage := &out{Type: "out", Message: strings.Trim(m, "\n ")}
				m, _ = outMessage.toJSON()
				fmt.Println(m)
			}
		} else {
			fmt.Println(fmt.Sprintf(msg, args...))
		}
	}
}

// Initialize initializes the logger object
func Initialize(isMachineReadable bool, logLevel string, c channel) {
	initialized = true
	level = loggingLevel(logLevel)
	activeLogger = logger(c)
	machineReadable = isMachineReadable
}

func logger(c channel) *logging.Logger {
	var l *logging.Logger
	switch c {
	case LSP:
		l = logging.MustGetLogger("gauge-lsp")
		initFileLogger(LspLogFileName, l)
		isLSP = true
		break
	case API:
		l = logging.MustGetLogger("gauge-api")
		initFileLogger(apiLogFileName, l)
		break
	default:
		l = logging.MustGetLogger("gauge")
		initFileLogger(gaugeLogFileName, l)
	}
	return l
}

func initFileLogger(logFileName string, fileLogger *logging.Logger) {
	var backend logging.Backend
	ActiveLogFile = getLogFile(logFileName)
	backend = createFileLogger(ActiveLogFile, 10)
	fileFormatter := logging.NewBackendFormatter(backend, fileLogFormat)
	fileLoggerLeveled := logging.AddModuleLevel(fileFormatter)
	fileLoggerLeveled.SetLevel(logging.DEBUG, "")

	fileLogger.SetBackend(fileLoggerLeveled)
}

func createFileLogger(name string, size int) logging.Backend {
	return logging.NewLogBackend(&lumberjack.Logger{
		Filename:   name,
		MaxSize:    size, // megabytes
		MaxBackups: 3,
		MaxAge:     28, //days
	}, "", 0)
}

func addLogsDirPath(logFileName string) string {
	customLogsDir := os.Getenv(logsDirectory)
	if customLogsDir == "" {
		return filepath.Join(logs, logFileName)
	}
	return filepath.Join(customLogsDir, logFileName)
}

func getLogFile(logFileName string) string {
	logDirPath := addLogsDirPath(logFileName)
	if filepath.IsAbs(logDirPath) {
		return logDirPath
	}
	if config.ProjectRoot != "" {
		return filepath.Join(config.ProjectRoot, logDirPath)
	}
	gaugeHome, err := common.GetGaugeHomeDirectory()
	if err != nil {
		return logDirPath
	}
	return filepath.Join(gaugeHome, logDirPath)
}

func loggingLevel(logLevel string) logging.Level {
	if logLevel != "" {
		switch strings.ToLower(logLevel) {
		case "debug":
			return logging.DEBUG
		case "info":
			return logging.INFO
		case "warning":
			return logging.WARNING
		case "error":
			return logging.ERROR
		}
	}
	return logging.INFO
}

// HandleWarningMessages logs multiple messages in WARNING mode
func HandleWarningMessages(stdout bool, warnings []string) {
	for _, warning := range warnings {
		Warning(stdout, warning)
	}
}
<commit_msg>Avoid trimming the new line char as we have already split the message on newline char. getgauge/gauge-visualstudio#254<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package logger

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/getgauge/gauge/plugin/pluginInfo"
	"github.com/getgauge/gauge/version"

	"runtime"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/natefinch/lumberjack"
	"github.com/op/go-logging"
)

// Channel specifies the logging channel. Can be one of CLI, API or LSP
type channel int

const (
	logsDirectory    = "logs_directory"
	logs             = "logs"
	gaugeLogFileName = "gauge.log"
	apiLogFileName   = "api.log"
	LspLogFileName   = "lsp.log"
	// CLI indicates gauge is used as a CLI.
	CLI channel = iota
	// API indicates gauge is in daemon mode. Used in IDEs.
	API
	// LSP indicates that gauge is acting as an LSP server.
	LSP
)

var level logging.Level
var activeLogger *logging.Logger
var fileLogFormat = logging.MustStringFormatter("%{time:02-01-2006 15:04:05.000} [Gauge] [%{level}] %{message}")
var isLSP bool
var initialized bool
var ActiveLogFile string
var machineReadable bool

// Info logs INFO messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Info(stdout bool, msg string) {
	Infof(stdout, msg)
}

// Infof logs INFO messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Infof(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		return
	}
	activeLogger.Infof(msg, args...)
}

// Error logs ERROR messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Error(stdout bool, msg string) {
	Errorf(stdout, msg)
}

// Errorf logs ERROR messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Errorf(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		fmt.Fprintf(os.Stderr, msg, args...)
		return
	}
	activeLogger.Errorf(msg, args...)
}

// Warning logs WARNING messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Warning(stdout bool, msg string) {
	Warningf(stdout, msg)
}

// Warningf logs WARNING messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Warningf(stdout bool, msg string, args ...interface{}) {
	write(stdout, msg, args...)
	if !initialized {
		return
	}
	activeLogger.Warningf(msg, args...)
}

// Fatal logs CRITICAL messages and exits. stdout flag indicates if message is to be written to stdout in addition to log.
func Fatal(stdout bool, msg string) {
	Fatalf(stdout, msg)
}

// Fatalf logs CRITICAL messages and exits. stdout flag indicates if message is to be written to stdout in addition to log.
func Fatalf(stdout bool, msg string, args ...interface{}) {
	message := getErrorText(msg, args...)
	if !initialized {
		fmt.Fprintf(os.Stderr, msg, args...)
		return
	}
	write(stdout, message)
	activeLogger.Fatalf(msg, args...)
}

// Debug logs DEBUG messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Debug(stdout bool, msg string) {
	Debugf(stdout, msg)
}

// Debugf logs DEBUG messages. stdout flag indicates if message is to be written to stdout in addition to log.
func Debugf(stdout bool, msg string, args ...interface{}) {
	if !initialized {
		return
	}
	activeLogger.Debugf(msg, args...)
	if level == logging.DEBUG {
		write(stdout, msg, args...)
	}
}

func getErrorText(msg string, args ...interface{}) string {
	env := []string{runtime.GOOS, version.FullVersion()}
	if version.GetCommitHash() != "" {
		env = append(env, version.GetCommitHash())
	}
	envText := strings.Join(env, ", ")
	return fmt.Sprintf(`Error ----------------------------------

%s

Get Support ----------------------------
	Docs:          https://docs.gauge.org
	Bugs:          https://github.com/getgauge/gauge/issues
	Chat:          https://gitter.im/getgauge/chat

Your Environment Information -----------
	%s
	%s`, fmt.Sprintf(msg, args...),
		envText,
		getPluginVersions())
}

func getPluginVersions() string {
	pis, err := pluginInfo.GetAllInstalledPluginsWithVersion()
	if err != nil {
		return fmt.Sprintf("Could not retrieve plugin information.")
	}
	pluginVersions := make([]string, 0, 0)
	for _, pi := range pis {
		pluginVersions = append(pluginVersions, fmt.Sprintf(`%s (%s)`, pi.Name, pi.Version))
	}
	return strings.Join(pluginVersions, ", ")
}

type out struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func (out *out) toJSON() (string, error) {
	json, err := json.Marshal(out)
	if err != nil {
		return "", err
	}
	return string(json), nil
}
func write(stdout bool, msg string, args ...interface{}) {
	if !isLSP && stdout {
		if machineReadable {
			strs := strings.Split(fmt.Sprintf(msg, args...), "\n")
			for _, m := range strs {
				outMessage := &out{Type: "out", Message: m}
				m, _ = outMessage.toJSON()
				fmt.Println(m)
			}
		} else {
			fmt.Println(fmt.Sprintf(msg, args...))
		}
	}
}

// Initialize initializes the logger object
func Initialize(isMachineReadable bool, logLevel string, c channel) {
	initialized = true
	level = loggingLevel(logLevel)
	activeLogger = logger(c)
	machineReadable = isMachineReadable
}

func logger(c channel) *logging.Logger {
	var l *logging.Logger
	switch c {
	case LSP:
		l = logging.MustGetLogger("gauge-lsp")
		initFileLogger(LspLogFileName, l)
		isLSP = true
		break
	case API:
		l = logging.MustGetLogger("gauge-api")
		initFileLogger(apiLogFileName, l)
		break
	default:
		l = logging.MustGetLogger("gauge")
		initFileLogger(gaugeLogFileName, l)
	}
	return l
}

func initFileLogger(logFileName string, fileLogger *logging.Logger) {
	var backend logging.Backend
	ActiveLogFile = getLogFile(logFileName)
	backend = createFileLogger(ActiveLogFile, 10)
	fileFormatter := logging.NewBackendFormatter(backend, fileLogFormat)
	fileLoggerLeveled := logging.AddModuleLevel(fileFormatter)
	fileLoggerLeveled.SetLevel(logging.DEBUG, "")

	fileLogger.SetBackend(fileLoggerLeveled)
}

func createFileLogger(name string, size int) logging.Backend {
	return logging.NewLogBackend(&lumberjack.Logger{
		Filename:   name,
		MaxSize:    size, // megabytes
		MaxBackups: 3,
		MaxAge:     28, //days
	}, "", 0)
}

func addLogsDirPath(logFileName string) string {
	customLogsDir := os.Getenv(logsDirectory)
	if customLogsDir == "" {
		return filepath.Join(logs, logFileName)
	}
	return filepath.Join(customLogsDir, logFileName)
}

func getLogFile(logFileName string) string {
	logDirPath := addLogsDirPath(logFileName)
	if filepath.IsAbs(logDirPath) {
		return logDirPath
	}
	if config.ProjectRoot != "" {
		return filepath.Join(config.ProjectRoot, logDirPath)
	}
	gaugeHome, err := common.GetGaugeHomeDirectory()
	if err != nil {
		return logDirPath
	}
	return filepath.Join(gaugeHome, logDirPath)
}

func loggingLevel(logLevel string) logging.Level {
	if logLevel != "" {
		switch strings.ToLower(logLevel) {
		case "debug":
			return logging.DEBUG
		case "info":
			return logging.INFO
		case "warning":
			return logging.WARNING
		case "error":
			return logging.ERROR
		}
	}
	return logging.INFO
}

// HandleWarningMessages logs multiple messages in WARNING mode
func HandleWarningMessages(stdout bool, warnings []string) {
	for _, warning := range warnings {
		Warning(stdout, warning)
	}
}
<|endoftext|>