<commit_before>/*
	Copyright (c) 2015-2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	datalog.go: Log stratux data as it is received. Bucket data into timestamp time slots.

*/

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const (
	LOG_TIMESTAMP_RESOLUTION = 250 * time.Millisecond
)

type StratuxTimestamp struct {
	Time_type_preference int // 0 = stratuxClock, 1 = gpsClock, 2 = gpsClock extrapolated via stratuxClock.
	StratuxClock_value   time.Time
	GPSClock_value       time.Time
	PreferredTime_value  time.Time
}

var dataLogTimestamps map[int64]StratuxTimestamp
var dataLogCurTimestamp int64 // Current timestamp bucket. This is an index on dataLogTimestamps which is not necessarily the db id.

/*
	checkTimestamp().
		Verify that our current timestamp is within the LOG_TIMESTAMP_RESOLUTION bucket.
		 Returns false if the timestamp was changed, true if it is still valid.
		 This is where GPS timestamps are extrapolated, if the GPS data is currently valid.
*/

func checkTimestamp() bool {
	if stratuxClock.Since(dataLogTimestamps[dataLogCurTimestamp].StratuxClock_value) >= LOG_TIMESTAMP_RESOLUTION {
		//FIXME: mutex.
		var ts StratuxTimestamp
		ts.id = 0
		ts.Time_type_preference = 0 // stratuxClock.
		ts.StratuxClock_value = stratuxClock.Time
		ts.GPSClock_value = time.Time{}
		ts.PreferredTime_value = stratuxClock.Time

		// Extrapolate from GPS timestamp, if possible.
		if isGPSClockValid() && dataLogCurTimestamp > 0 {
			// Was the last timestamp either extrapolated or GPS time?
			last_ts := dataLogTimestamps[dataLogCurTimestamp]
			if last_ts.Time_type_preference == 1 || last_ts.Time_type_preference == 2 {
				// Extrapolate via stratuxClock.
				timeSinceLastTS := ts.StratuxClock_value.Sub(last_ts.StratuxClock_value) // stratuxClock ticks since last timestamp.
				extrapolatedGPSTimestamp := last_ts.PreferredTime_value.Add(timeSinceLastTS)

				// Re-set the preferred timestamp type to '2' (extrapolated time).
				ts.Time_type_preference = 2
				ts.PreferredTime_value = extrapolatedGPSTimestamp
			}
		}

		dataLogCurTimestamp++
		dataLogTimestamps[dataLogCurTimestamp] = ts

		return false
	}
	return true
}

type SQLiteMarshal struct {
	FieldType string
	Marshal   func(v reflect.Value) string
}

func boolMarshal(v reflect.Value) string {
	b := v.Bool()
	if b {
		return "1"
	}
	return "0"
}

func intMarshal(v reflect.Value) string {
	return strconv.FormatInt(v.Int(), 10)
}

func uintMarshal(v reflect.Value) string {
	return strconv.FormatUint(v.Uint(), 10)
}

func floatMarshal(v reflect.Value) string {
	return strconv.FormatFloat(v.Float(), 'f', 10, 64)
}

func stringMarshal(v reflect.Value) string {
	return v.String()
}

func notsupportedMarshal(v reflect.Value) string {
	return ""
}

func structCanBeMarshalled(v reflect.Value) bool {
	m := v.MethodByName("String")
	if m.IsValid() && !m.IsNil() {
		return true
	}
	return false
}

func structMarshal(v reflect.Value) string {
	if structCanBeMarshalled(v) {
		m := v.MethodByName("String")
		in := make([]reflect.Value, 0)
		ret := m.Call(in)
		if len(ret) > 0 {
			return ret[0].String()
		}
	}
	return ""
}

var sqliteMarshalFunctions = map[string]SQLiteMarshal{
	"bool":         {FieldType: "INTEGER", Marshal: boolMarshal},
	"int":          {FieldType: "INTEGER", Marshal: intMarshal},
	"uint":         {FieldType: "INTEGER", Marshal: uintMarshal},
	"float":        {FieldType: "REAL", Marshal: floatMarshal},
	"string":       {FieldType: "TEXT", Marshal: stringMarshal},
	"struct":       {FieldType: "STRING", Marshal: structMarshal},
	"notsupported": {FieldType: "notsupported", Marshal: notsupportedMarshal},
}

var sqlTypeMap = map[reflect.Kind]string{
	reflect.Bool:          "bool",
	reflect.Int:           "int",
	reflect.Int8:          "int",
	reflect.Int16:         "int",
	reflect.Int32:         "int",
	reflect.Int64:         "int",
	reflect.Uint:          "uint",
	reflect.Uint8:         "uint",
	reflect.Uint16:        "uint",
	reflect.Uint32:        "uint",
	reflect.Uint64:        "uint",
	reflect.Uintptr:       "notsupported",
	reflect.Float32:       "float",
	reflect.Float64:       "float",
	reflect.Complex64:     "notsupported",
	reflect.Complex128:    "notsupported",
	reflect.Array:         "notsupported",
	reflect.Chan:          "notsupported",
	reflect.Func:          "notsupported",
	reflect.Interface:     "notsupported",
	reflect.Map:           "notsupported",
	reflect.Ptr:           "notsupported",
	reflect.Slice:         "notsupported",
	reflect.String:        "string",
	reflect.Struct:        "struct",
	reflect.UnsafePointer: "notsupported",
}

func makeTable(i interface{}, tbl string, db *sql.DB) {
	val := reflect.ValueOf(i)

	fields := make([]string, 0)
	for i := 0; i < val.NumField(); i++ {
		kind := val.Field(i).Kind()
		fieldName := val.Type().Field(i).Name
		sqlTypeAlias := sqlTypeMap[kind]

		// Check that if the field is a struct that it can be marshalled.
		if sqlTypeAlias == "struct" && !structCanBeMarshalled(val.Field(i)) {
			continue
		}
		if sqlTypeAlias == "notsupported" || fieldName == "id" {
			continue
		}
		sqlType := sqliteMarshalFunctions[sqlTypeAlias].FieldType
		s := fieldName + " " + sqlType
		fields = append(fields, s)
	}

	// Add the timestamp_id field to link up with the timestamp table.
	if tbl != "timestamp" {
		fields = append(fields, "timestamp_id INTEGER")
	}

	tblCreate := fmt.Sprintf("CREATE TABLE %s (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, %s)", tbl, strings.Join(fields, ", "))
	_, err := db.Exec(tblCreate)
	if err != nil {
		fmt.Printf("ERROR: %s\n", err.Error())
	}
}

func insertData(i interface{}, tbl string, db *sql.DB, ts_num int64) int64 {
	val := reflect.ValueOf(i)

	keys := make([]string, 0)
	values := make([]string, 0)
	for i := 0; i < val.NumField(); i++ {
		kind := val.Field(i).Kind()
		fieldName := val.Type().Field(i).Name
		sqlTypeAlias := sqlTypeMap[kind]

		if sqlTypeAlias == "notsupported" || fieldName == "id" {
			continue
		}

		v := sqliteMarshalFunctions[sqlTypeAlias].Marshal(val.Field(i))

		keys = append(keys, fieldName)
		values = append(values, v)
	}

	// Add the timestamp_id field to link up with the timestamp table.
	if tbl != "timestamp" {
		keys = append(keys, "timestamp_id")
		if dataLogTimestamps[ts_num].id == 0 {
			//FIXME: This is somewhat convoluted. When insertData() is called for a ts_num that corresponds to a timestamp with no database id,
			// then it inserts that timestamp via the same interface and the id is updated in the structure via the below lines
			// (dataLogTimestamps[ts_num].id = id).
			insertData(dataLogTimestamps[ts_num], "timestamp", db, ts_num) // Updates dataLogTimestamps[ts_num].id.
		}
		values = append(values, strconv.FormatInt(dataLogTimestamps[ts_num].id, 10))
	}

	tblInsert := fmt.Sprintf("INSERT INTO %s (%s) VALUES(%s)", tbl, strings.Join(keys, ","),
		strings.Join(strings.Split(strings.Repeat("?", len(keys)), ""), ","))

	ifs := make([]interface{}, len(values))
	for i := 0; i < len(values); i++ {
		ifs[i] = values[i]
	}
	res, err := db.Exec(tblInsert, ifs...)
	if err != nil {
		log.Printf("ERROR: %s\n", err.Error())
	}
	id, err := res.LastInsertId()
	if err == nil {
		if tbl == "timestamp" {
			dataLogTimestamps[ts_num].id = id
		}
		return id
	}

	return 0
}

type DataLogRow struct {
	tbl    string
	data   interface{}
	ts_num int64
}

var dataLogChan chan DataLogRow
var shutdownDataLog chan bool

func dataLogWriter() {
	dataLogChan = make(chan DataLogRow, 10240)
	shutdownDataLog = make(chan bool)
	dataLogTimestamps = make(map[int64]StratuxTimestamp, 0)

	// Check if we need to create a new database.
	createDatabase := false

	if _, err := os.Stat(dataLogFile); os.IsNotExist(err) {
		createDatabase = true
		log.Printf("creating new database '%s'.\n", dataLogFile)
	}

	db, err := sql.Open("sqlite3", dataLogFile)
	if err != nil {
		log.Printf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	// Do we need to create the database?
	if createDatabase {
		makeTable(StratuxTimestamp{}, "timestamp", db)
		makeTable(mySituation, "mySituation", db)
		makeTable(globalStatus, "status", db)
		makeTable(globalSettings, "settings", db)
		makeTable(TrafficInfo{}, "traffic", db)
		makeTable(msg{}, "messages", db)
		makeTable(Dump1090TermMessage{}, "dump1090_terminal", db)
	}

	// The write queue. As data comes in via dataLogChan, it is timestamped and stored.
	//  When writeTicker comes up, the queue is emptied.
	rowsQueuedForWrite := make([]DataLogRow, 0)
	writeTicker := time.NewTicker(5 * time.Second)

	for {
		select {
		//FIXME: measure latency from here to end of block. Messages may need to be timestamped *before* executing everything here.
		case r := <-dataLogChan:

			// Check if our time bucket has expired or has never been entered.
			checkTimestamp()
			// Mark the row with the current timestamp ID, in case it gets entered later.
			r.ts_num = dataLogCurTimestamp
			// Queue it for the scheduled write.
			rowsQueuedForWrite = append(rowsQueuedForWrite, r)
		case <-writeTicker.C:
			for _, r := range rowsQueuedForWrite {
				insertData(r.data, r.tbl, db, r.ts_num)
			}
			rowsQueuedForWrite = make([]DataLogRow, 0) // Zero the queue.
		case <-shutdownDataLog: // Received a message on the channel (anything). Graceful shutdown (defer statement).
			return
		}
	}
}

/*
	setDataLogTimeWithGPS().
		Create a timestamp entry using GPS time.
*/

func setDataLogTimeWithGPS(sit SituationData) {
	if isGPSClockValid() {
		//FIXME: mutex.
		var ts StratuxTimestamp
		// Piggyback a GPS time update from this update.
		ts.id = 0
		ts.Time_type_preference = 1 // gpsClock.
		ts.StratuxClock_value = stratuxClock.Time
		ts.GPSClock_value = sit.GPSTime
		ts.PreferredTime_value = sit.GPSTime
		dataLogCurTimestamp++
		dataLogTimestamps[dataLogCurTimestamp] = ts
	}
}

func logSituation() {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "mySituation", data: mySituation}
	}
}

func logStatus() {
	dataLogChan <- DataLogRow{tbl: "status", data: globalStatus}
}

func logSettings() {
	dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings}
}

func logTraffic(ti TrafficInfo) {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "traffic", data: ti}
	}
}

func logMsg(m msg) {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "messages", data: m}
	}
}

func logDump1090TermMessage(m Dump1090TermMessage) {
	dataLogChan <- DataLogRow{tbl: "dump1090_terminal", data: m}
}

func initDataLog() {
	go dataLogWriter()
}
<commit_msg>Typo.<commit_after>/*
	Copyright (c) 2015-2016 Christopher Young
	Distributable under the terms of The "BSD New"" License
	that can be found in the LICENSE file, herein included
	as part of this header.

	datalog.go: Log stratux data as it is received. Bucket data into timestamp time slots.

*/

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const (
	LOG_TIMESTAMP_RESOLUTION = 250 * time.Millisecond
)

type StratuxTimestamp struct {
	id                   int64
	Time_type_preference int // 0 = stratuxClock, 1 = gpsClock, 2 = gpsClock extrapolated via stratuxClock.
	StratuxClock_value   time.Time
	GPSClock_value       time.Time
	PreferredTime_value  time.Time
}

var dataLogTimestamps map[int64]StratuxTimestamp
var dataLogCurTimestamp int64 // Current timestamp bucket. This is an index on dataLogTimestamps which is not necessarily the db id.

/*
	checkTimestamp().
		Verify that our current timestamp is within the LOG_TIMESTAMP_RESOLUTION bucket.
		 Returns false if the timestamp was changed, true if it is still valid.
		 This is where GPS timestamps are extrapolated, if the GPS data is currently valid.
*/

func checkTimestamp() bool {
	if stratuxClock.Since(dataLogTimestamps[dataLogCurTimestamp].StratuxClock_value) >= LOG_TIMESTAMP_RESOLUTION {
		//FIXME: mutex.
		var ts StratuxTimestamp
		ts.id = 0
		ts.Time_type_preference = 0 // stratuxClock.
		ts.StratuxClock_value = stratuxClock.Time
		ts.GPSClock_value = time.Time{}
		ts.PreferredTime_value = stratuxClock.Time

		// Extrapolate from GPS timestamp, if possible.
		if isGPSClockValid() && dataLogCurTimestamp > 0 {
			// Was the last timestamp either extrapolated or GPS time?
			last_ts := dataLogTimestamps[dataLogCurTimestamp]
			if last_ts.Time_type_preference == 1 || last_ts.Time_type_preference == 2 {
				// Extrapolate via stratuxClock.
				timeSinceLastTS := ts.StratuxClock_value.Sub(last_ts.StratuxClock_value) // stratuxClock ticks since last timestamp.
				extrapolatedGPSTimestamp := last_ts.PreferredTime_value.Add(timeSinceLastTS)

				// Re-set the preferred timestamp type to '2' (extrapolated time).
				ts.Time_type_preference = 2
				ts.PreferredTime_value = extrapolatedGPSTimestamp
			}
		}

		dataLogCurTimestamp++
		dataLogTimestamps[dataLogCurTimestamp] = ts

		return false
	}
	return true
}

type SQLiteMarshal struct {
	FieldType string
	Marshal   func(v reflect.Value) string
}

func boolMarshal(v reflect.Value) string {
	b := v.Bool()
	if b {
		return "1"
	}
	return "0"
}

func intMarshal(v reflect.Value) string {
	return strconv.FormatInt(v.Int(), 10)
}

func uintMarshal(v reflect.Value) string {
	return strconv.FormatUint(v.Uint(), 10)
}

func floatMarshal(v reflect.Value) string {
	return strconv.FormatFloat(v.Float(), 'f', 10, 64)
}

func stringMarshal(v reflect.Value) string {
	return v.String()
}

func notsupportedMarshal(v reflect.Value) string {
	return ""
}

func structCanBeMarshalled(v reflect.Value) bool {
	m := v.MethodByName("String")
	if m.IsValid() && !m.IsNil() {
		return true
	}
	return false
}

func structMarshal(v reflect.Value) string {
	if structCanBeMarshalled(v) {
		m := v.MethodByName("String")
		in := make([]reflect.Value, 0)
		ret := m.Call(in)
		if len(ret) > 0 {
			return ret[0].String()
		}
	}
	return ""
}

var sqliteMarshalFunctions = map[string]SQLiteMarshal{
	"bool":         {FieldType: "INTEGER", Marshal: boolMarshal},
	"int":          {FieldType: "INTEGER", Marshal: intMarshal},
	"uint":         {FieldType: "INTEGER", Marshal: uintMarshal},
	"float":        {FieldType: "REAL", Marshal: floatMarshal},
	"string":       {FieldType: "TEXT", Marshal: stringMarshal},
	"struct":       {FieldType: "STRING", Marshal: structMarshal},
	"notsupported": {FieldType: "notsupported", Marshal: notsupportedMarshal},
}

var sqlTypeMap = map[reflect.Kind]string{
	reflect.Bool:          "bool",
	reflect.Int:           "int",
	reflect.Int8:          "int",
	reflect.Int16:         "int",
	reflect.Int32:         "int",
	reflect.Int64:         "int",
	reflect.Uint:          "uint",
	reflect.Uint8:         "uint",
	reflect.Uint16:        "uint",
	reflect.Uint32:        "uint",
	reflect.Uint64:        "uint",
	reflect.Uintptr:       "notsupported",
	reflect.Float32:       "float",
	reflect.Float64:       "float",
	reflect.Complex64:     "notsupported",
	reflect.Complex128:    "notsupported",
	reflect.Array:         "notsupported",
	reflect.Chan:          "notsupported",
	reflect.Func:          "notsupported",
	reflect.Interface:     "notsupported",
	reflect.Map:           "notsupported",
	reflect.Ptr:           "notsupported",
	reflect.Slice:         "notsupported",
	reflect.String:        "string",
	reflect.Struct:        "struct",
	reflect.UnsafePointer: "notsupported",
}

func makeTable(i interface{}, tbl string, db *sql.DB) {
	val := reflect.ValueOf(i)

	fields := make([]string, 0)
	for i := 0; i < val.NumField(); i++ {
		kind := val.Field(i).Kind()
		fieldName := val.Type().Field(i).Name
		sqlTypeAlias := sqlTypeMap[kind]

		// Check that if the field is a struct that it can be marshalled.
		if sqlTypeAlias == "struct" && !structCanBeMarshalled(val.Field(i)) {
			continue
		}
		if sqlTypeAlias == "notsupported" || fieldName == "id" {
			continue
		}
		sqlType := sqliteMarshalFunctions[sqlTypeAlias].FieldType
		s := fieldName + " " + sqlType
		fields = append(fields, s)
	}

	// Add the timestamp_id field to link up with the timestamp table.
	if tbl != "timestamp" {
		fields = append(fields, "timestamp_id INTEGER")
	}

	tblCreate := fmt.Sprintf("CREATE TABLE %s (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, %s)", tbl, strings.Join(fields, ", "))
	_, err := db.Exec(tblCreate)
	if err != nil {
		fmt.Printf("ERROR: %s\n", err.Error())
	}
}

func insertData(i interface{}, tbl string, db *sql.DB, ts_num int64) int64 {
	val := reflect.ValueOf(i)

	keys := make([]string, 0)
	values := make([]string, 0)
	for i := 0; i < val.NumField(); i++ {
		kind := val.Field(i).Kind()
		fieldName := val.Type().Field(i).Name
		sqlTypeAlias := sqlTypeMap[kind]

		if sqlTypeAlias == "notsupported" || fieldName == "id" {
			continue
		}

		v := sqliteMarshalFunctions[sqlTypeAlias].Marshal(val.Field(i))

		keys = append(keys, fieldName)
		values = append(values, v)
	}

	// Add the timestamp_id field to link up with the timestamp table.
	if tbl != "timestamp" {
		keys = append(keys, "timestamp_id")
		if dataLogTimestamps[ts_num].id == 0 {
			//FIXME: This is somewhat convoluted. When insertData() is called for a ts_num that corresponds to a timestamp with no database id,
			// then it inserts that timestamp via the same interface and the id is updated in the structure via the below lines
			// (dataLogTimestamps[ts_num].id = id).
			insertData(dataLogTimestamps[ts_num], "timestamp", db, ts_num) // Updates dataLogTimestamps[ts_num].id.
		}
		values = append(values, strconv.FormatInt(dataLogTimestamps[ts_num].id, 10))
	}

	tblInsert := fmt.Sprintf("INSERT INTO %s (%s) VALUES(%s)", tbl, strings.Join(keys, ","),
		strings.Join(strings.Split(strings.Repeat("?", len(keys)), ""), ","))

	ifs := make([]interface{}, len(values))
	for i := 0; i < len(values); i++ {
		ifs[i] = values[i]
	}
	res, err := db.Exec(tblInsert, ifs...)
	if err != nil {
		log.Printf("ERROR: %s\n", err.Error())
	}
	id, err := res.LastInsertId()
	if err == nil {
		if tbl == "timestamp" {
			dataLogTimestamps[ts_num].id = id
		}
		return id
	}

	return 0
}

type DataLogRow struct {
	tbl    string
	data   interface{}
	ts_num int64
}

var dataLogChan chan DataLogRow
var shutdownDataLog chan bool

func dataLogWriter() {
	dataLogChan = make(chan DataLogRow, 10240)
	shutdownDataLog = make(chan bool)
	dataLogTimestamps = make(map[int64]StratuxTimestamp, 0)

	// Check if we need to create a new database.
	createDatabase := false

	if _, err := os.Stat(dataLogFile); os.IsNotExist(err) {
		createDatabase = true
		log.Printf("creating new database '%s'.\n", dataLogFile)
	}

	db, err := sql.Open("sqlite3", dataLogFile)
	if err != nil {
		log.Printf("sql.Open(): %s\n", err.Error())
	}
	defer db.Close()

	// Do we need to create the database?
	if createDatabase {
		makeTable(StratuxTimestamp{}, "timestamp", db)
		makeTable(mySituation, "mySituation", db)
		makeTable(globalStatus, "status", db)
		makeTable(globalSettings, "settings", db)
		makeTable(TrafficInfo{}, "traffic", db)
		makeTable(msg{}, "messages", db)
		makeTable(Dump1090TermMessage{}, "dump1090_terminal", db)
	}

	// The write queue. As data comes in via dataLogChan, it is timestamped and stored.
	//  When writeTicker comes up, the queue is emptied.
	rowsQueuedForWrite := make([]DataLogRow, 0)
	writeTicker := time.NewTicker(5 * time.Second)

	for {
		select {
		//FIXME: measure latency from here to end of block. Messages may need to be timestamped *before* executing everything here.
		case r := <-dataLogChan:

			// Check if our time bucket has expired or has never been entered.
			checkTimestamp()
			// Mark the row with the current timestamp ID, in case it gets entered later.
			r.ts_num = dataLogCurTimestamp
			// Queue it for the scheduled write.
			rowsQueuedForWrite = append(rowsQueuedForWrite, r)
		case <-writeTicker.C:
			for _, r := range rowsQueuedForWrite {
				insertData(r.data, r.tbl, db, r.ts_num)
			}
			rowsQueuedForWrite = make([]DataLogRow, 0) // Zero the queue.
		case <-shutdownDataLog: // Received a message on the channel (anything). Graceful shutdown (defer statement).
			return
		}
	}
}

/*
	setDataLogTimeWithGPS().
		Create a timestamp entry using GPS time.
*/

func setDataLogTimeWithGPS(sit SituationData) {
	if isGPSClockValid() {
		//FIXME: mutex.
		var ts StratuxTimestamp
		// Piggyback a GPS time update from this update.
		ts.id = 0
		ts.Time_type_preference = 1 // gpsClock.
		ts.StratuxClock_value = stratuxClock.Time
		ts.GPSClock_value = sit.GPSTime
		ts.PreferredTime_value = sit.GPSTime
		dataLogCurTimestamp++
		dataLogTimestamps[dataLogCurTimestamp] = ts
	}
}

func logSituation() {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "mySituation", data: mySituation}
	}
}

func logStatus() {
	dataLogChan <- DataLogRow{tbl: "status", data: globalStatus}
}

func logSettings() {
	dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings}
}

func logTraffic(ti TrafficInfo) {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "traffic", data: ti}
	}
}

func logMsg(m msg) {
	if globalSettings.ReplayLog {
		dataLogChan <- DataLogRow{tbl: "messages", data: m}
	}
}

func logDump1090TermMessage(m Dump1090TermMessage) {
	dataLogChan <- DataLogRow{tbl: "dump1090_terminal", data: m}
}

func initDataLog() {
	go dataLogWriter()
}
<|endoftext|>


<commit_before>package quic

import (
	"bytes"
	"fmt"
	"net"
	"sync"

	"github.com/lucas-clemente/quic-go/internal/utils"
)

var (
	connMuxerOnce sync.Once
	connMuxer     multiplexer
)

type multiplexer interface {
	AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte) (packetHandlerManager, error)
	RemoveConn(net.PacketConn) error
}

type connManager struct {
	connIDLen         int
	statelessResetKey []byte
	manager           packetHandlerManager
}

// The connMultiplexer listens on multiple net.PacketConns and dispatches
// incoming packets to the session handler.
type connMultiplexer struct {
	mutex sync.Mutex

	conns                   map[string] /* LocalAddr().String() */ connManager
	newPacketHandlerManager func(net.PacketConn, int, []byte, utils.Logger) packetHandlerManager // so it can be replaced in the tests

	logger utils.Logger
}

var _ multiplexer = &connMultiplexer{}

func getMultiplexer() multiplexer {
	connMuxerOnce.Do(func() {
		connMuxer = &connMultiplexer{
			conns:                   make(map[string]connManager),
			logger:                  utils.DefaultLogger.WithPrefix("muxer"),
			newPacketHandlerManager: newPacketHandlerMap,
		}
	})
	return connMuxer
}

func (m *connMultiplexer) AddConn(
	c net.PacketConn,
	connIDLen int,
	statelessResetKey []byte,
) (packetHandlerManager, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	laddr := c.LocalAddr().String()
	p, ok := m.conns[laddr]
	if !ok {
		manager := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, m.logger)
		p = connManager{
			connIDLen:         connIDLen,
			statelessResetKey: statelessResetKey,
			manager:           manager,
		}
		m.conns[laddr] = p
	}
	if p.connIDLen != connIDLen {
		return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
	}
	if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) {
		return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
	}
	return p.manager, nil
}

func (m *connMultiplexer) RemoveConn(c net.PacketConn) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	laddr := c.LocalAddr().String()
	if _, ok := m.conns[laddr]; !ok {
		return fmt.Errorf("cannote remove connection, connection is unknown")
	}

	delete(m.conns, laddr)
	return nil
}
<commit_msg>add network to multiplexer index<commit_after>package quic

import (
	"bytes"
	"fmt"
	"net"
	"sync"

	"github.com/lucas-clemente/quic-go/internal/utils"
)

var (
	connMuxerOnce sync.Once
	connMuxer     multiplexer
)

type multiplexer interface {
	AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte) (packetHandlerManager, error)
	RemoveConn(net.PacketConn) error
}

type connManager struct {
	connIDLen         int
	statelessResetKey []byte
	manager           packetHandlerManager
}

// The connMultiplexer listens on multiple net.PacketConns and dispatches
// incoming packets to the session handler.
type connMultiplexer struct {
	mutex sync.Mutex

	conns                   map[string] /* LocalAddr().String() */ connManager
	newPacketHandlerManager func(net.PacketConn, int, []byte, utils.Logger) packetHandlerManager // so it can be replaced in the tests

	logger utils.Logger
}

var _ multiplexer = &connMultiplexer{}

func getMultiplexer() multiplexer {
	connMuxerOnce.Do(func() {
		connMuxer = &connMultiplexer{
			conns:                   make(map[string]connManager),
			logger:                  utils.DefaultLogger.WithPrefix("muxer"),
			newPacketHandlerManager: newPacketHandlerMap,
		}
	})
	return connMuxer
}

func (m *connMultiplexer) AddConn(
	c net.PacketConn,
	connIDLen int,
	statelessResetKey []byte,
) (packetHandlerManager, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	connIndex := c.LocalAddr().Network() + " " + c.LocalAddr().String()
	p, ok := m.conns[connIndex]
	if !ok {
		manager := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, m.logger)
		p = connManager{
			connIDLen:         connIDLen,
			statelessResetKey: statelessResetKey,
			manager:           manager,
		}
		m.conns[connIndex] = p
	}
	if p.connIDLen != connIDLen {
		return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
	}
	if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) {
		return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
	}
	return p.manager, nil
}

func (m *connMultiplexer) RemoveConn(c net.PacketConn) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	connIndex := c.LocalAddr().Network() + " " + c.LocalAddr().String()
	if _, ok := m.conns[connIndex]; !ok {
		return fmt.Errorf("cannote remove connection, connection is unknown")
	}

	delete(m.conns, connIndex)
	return nil
}
<|endoftext|>


<commit_before>package clusterdesc

import (
	"fmt"
	"strings"
)

type nodeRoleCondition func(n Node) string

// SelectNodes input node role condition,
// ouptut hostname range
func (c *Cluster) SelectNodes(f func(n *Node) string) string {
	var ret []string
	for _, n := range c.Nodes {
		t := f(&n)
		if len(t) > 0 {
			ret = append(ret, t)
		}
	}
	return strings.Join(ret, ",")
}

// InitialEtcdCluster derives the value of command line parameter
// --initial_cluster of etcd from Cluter.Nodes and Node.EtcdMember.
// NOTE: Every node in the cluster will have a etcd daemon running --
// either as a member or as a proxy.
func (c *Cluster) InitialEtcdCluster() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			name := n.Hostname()
			addr := n.Hostname()
			return fmt.Sprintf("%s=http://%s:2380", name, addr)
		}
		return ""
	})
}

// GetEtcdEndpoints fetch etcd cluster endpoints
func (c *Cluster) GetEtcdEndpoints() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			addr := n.Hostname()
			return fmt.Sprintf("http://%s:4001", addr)
		}
		return ""
	})
}

// GetEtcdMachines return the etcd members
func (c *Cluster) GetEtcdMachines() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			if len(n.Hostname()) > 0 {
				return fmt.Sprintf("http://%s:2379", n.Hostname())
			}
		}
		return ""
	})
}
<commit_msg>use index instead of iterator<commit_after>package clusterdesc

import (
	"fmt"
	"strings"
)

// SelectNodes input node role condition,
// ouptut hostname range
func (c *Cluster) SelectNodes(f func(n *Node) string) string {
	var ret []string
	for i := range c.Nodes {
		t := f(&(c.Nodes[i]))
		if len(t) > 0 {
			ret = append(ret, t)
		}
	}
	return strings.Join(ret, ",")
}

// InitialEtcdCluster derives the value of command line parameter
// --initial_cluster of etcd from Cluter.Nodes and Node.EtcdMember.
// NOTE: Every node in the cluster will have a etcd daemon running --
// either as a member or as a proxy.
func (c *Cluster) InitialEtcdCluster() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			name := n.Hostname()
			addr := n.Hostname()
			return fmt.Sprintf("%s=http://%s:2380", name, addr)
		}
		return ""
	})
}

// GetEtcdEndpoints fetch etcd cluster endpoints
func (c *Cluster) GetEtcdEndpoints() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			addr := n.Hostname()
			return fmt.Sprintf("http://%s:4001", addr)
		}
		return ""
	})
}

// GetEtcdMachines return the etcd members
func (c *Cluster) GetEtcdMachines() string {
	return c.SelectNodes(func(n *Node) string {
		if n.EtcdMember {
			if len(n.Hostname()) > 0 {
				return fmt.Sprintf("http://%s:2379", n.Hostname())
			}
		}
		return ""
	})
}
<|endoftext|>


<commit_before>package va

import (
	"crypto/sha256"
	"crypto/subtle"
	"crypto/tls"
	"crypto/x509"
	"encoding/asn1"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/jmhodges/clock"
	"github.com/letsencrypt/pebble/acme"
	"github.com/letsencrypt/pebble/core"
)

const (
	whitespaceCutset = "\n\r\t"
	userAgentBase    = "LetsEncrypt-Pebble-VA"

	// How long do valid authorizations last before expiring?
	validAuthzExpire = time.Hour

	// How many vaTasks can be in the channel before the WFE blocks on adding
	// another?
	taskQueueSize = 6

	// How many concurrent validations are performed?
	concurrentValidations = 3

	// noSleepEnvVar defines the environment variable name used to signal that the
	// VA should *not* sleep between validation attempts. Set this to 1 when you
	// invoke Pebble if you wish validation to be done at full speed, e.g.:
	//   PEBBLE_VA_NOSLEEP=1 pebble
	noSleepEnvVar = "PEBBLE_VA_NOSLEEP"
)

var IdPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}

func userAgent() string {
	return fmt.Sprintf(
		"%s (%s; %s)",
		userAgentBase, runtime.GOOS, runtime.GOARCH)
}

// certNames collects up all of a certificate's subject names (Subject CN and
// Subject Alternate Names) and reduces them to a comma joined string.
func certNames(cert *x509.Certificate) string {
	var names []string
	if cert.Subject.CommonName != "" {
		names = append(names, cert.Subject.CommonName)
	}
	names = append(names, cert.DNSNames...)
	return strings.Join(names, ", ")
}

type vaTask struct {
	Identifier string
	Challenge  *core.Challenge
	Account    *core.Account
}

type VAImpl struct {
	log      *log.Logger
	clk      clock.Clock
	httpPort int
	tlsPort  int
	tasks    chan *vaTask
	sleep    bool
}

func New(
	log *log.Logger,
	clk clock.Clock,
	httpPort, tlsPort int) *VAImpl {
	va := &VAImpl{
		log:      log,
		clk:      clk,
		httpPort: httpPort,
		tlsPort:  tlsPort,
		tasks:    make(chan *vaTask, taskQueueSize),
		sleep:    true,
	}

	// Read the PEBBLE_VA_NOSLEEP environment variable string
	noSleep := os.Getenv(noSleepEnvVar)
	// If it is set to something true-like, then the VA shouldn't sleep
	switch noSleep {
	case "1", "true", "True", "TRUE":
		va.sleep = false
		va.log.Printf("Disabling random VA sleeps")
	}

	go va.processTasks()
	return va
}

func (va VAImpl) ValidateChallenge(ident string, chal *core.Challenge, acct *core.Account) {
	task := &vaTask{
		Identifier: ident,
		Challenge:  chal,
		Account:    acct,
	}
	// Submit the task for validation
	va.tasks <- task
}

func (va VAImpl) processTasks() {
	for task := range va.tasks {
		go va.process(task)
	}
}

func (va VAImpl) firstError(results chan *core.ValidationRecord) *acme.ProblemDetails {
	for i := 0; i < concurrentValidations; i++ {
		result := <-results
		if result.Error != nil {
			return result.Error
		}
	}
	return nil
}

func (va VAImpl) process(task *vaTask) {
	va.log.Printf("Pulled a task from the Tasks queue: %#v", task)
	va.log.Printf("Starting %d validations.", concurrentValidations)

	chal := task.Challenge
	chal.Lock()
	// Update the validated date for the challenge
	now := va.clk.Now().UTC()
	chal.ValidatedDate = now
	chal.Validated = chal.ValidatedDate.Format(time.RFC3339)
	authz := chal.Authz
	chal.Unlock()

	results := make(chan *core.ValidationRecord, concurrentValidations)

	// Start a number of go routines to perform concurrent validations
	for i := 0; i < concurrentValidations; i++ {
		go va.performValidation(task, results)
	}

	err := va.firstError(results)
	// If one of the results was an error, the challenge fails
	if err != nil {
		// Lock the challenge to update the error & status
		chal.Lock()
		// Update the challenge Error
		chal.Error = err
		// Set the challenge and authorization to invalid
		chal.Status = acme.StatusInvalid
		chal.Unlock()

		// Lock the authz to update the authz status
		authz.Lock()
		authz.Status = acme.StatusInvalid
		authz.Unlock()

		va.log.Printf("authz %s set INVALID by completed challenge %s", authz.ID, chal.ID)
		// Return immediately - there's no need to check for order issuance
		return
	} else {
		// If none of the results were an error then the challenge succeeded.
		// Update the expiry for the valid authorization
		authz.Lock()
		authz.ExpiresDate = now.Add(validAuthzExpire)
		authz.Expires = authz.ExpiresDate.Format(time.RFC3339)
		authz.Status = acme.StatusValid
		authz.Unlock()

		// Set the authorization & challenge to valid
		chal.Lock()
		chal.Status = acme.StatusValid
		chal.Unlock()

		va.log.Printf("authz %s set VALID by completed challenge %s", authz.ID, chal.ID)
	}
}

func (va VAImpl) performValidation(task *vaTask, results chan<- *core.ValidationRecord) {
	if va.sleep {
		// Sleep for a random amount of time between 1-15s
		len := time.Duration(rand.Intn(15))
		va.log.Printf("Sleeping for %s seconds before validating", time.Second*len)
		va.clk.Sleep(time.Second * len)
	}

	switch task.Challenge.Type {
	case acme.ChallengeHTTP01:
		results <- va.validateHTTP01(task)
	case acme.ChallengeTLSALPN01:
		results <- va.validateTLSALPN01(task)
	case acme.ChallengeDNS01:
		results <- va.validateDNS01(task)
	default:
		va.log.Printf("Error: performValidation(): Invalid challenge type: %q", task.Challenge.Type)
	}
}

func (va VAImpl) validateDNS01(task *vaTask) *core.ValidationRecord {
	const dns01Prefix = "_acme-challenge"
	challengeSubdomain := fmt.Sprintf("%s.%s", dns01Prefix, task.Identifier)

	result := &core.ValidationRecord{
		URL:         challengeSubdomain,
		ValidatedAt: va.clk.Now(),
	}

	txts, err := net.LookupTXT(challengeSubdomain)
	if err != nil {
		result.Error = acme.UnauthorizedProblem("Error retrieving TXT records for DNS challenge")
		return result
	}

	if len(txts) == 0 {
		msg := fmt.Sprintf("No TXT records found for DNS challenge")
		result.Error = acme.UnauthorizedProblem(msg)
		return result
	}

	task.Challenge.RLock()
	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	h := sha256.Sum256([]byte(expectedKeyAuthorization))
	task.Challenge.RUnlock()
	authorizedKeysDigest := base64.RawURLEncoding.EncodeToString(h[:])

	for _, element := range txts {
		if subtle.ConstantTimeCompare([]byte(element), []byte(authorizedKeysDigest)) == 1 {
			return result
		}
	}

	msg := fmt.Sprintf("Correct value not found for DNS challenge")
	result.Error = acme.UnauthorizedProblem(msg)
	return result
}

func (va VAImpl) validateTLSALPN01(task *vaTask) *core.ValidationRecord {
	portString := strconv.Itoa(va.tlsPort)
	hostPort := net.JoinHostPort(task.Identifier, portString)

	result := &core.ValidationRecord{
		URL:         hostPort,
		ValidatedAt: va.clk.Now(),
	}

	certs, problem := va.fetchCerts(hostPort, &tls.Config{
		ServerName: task.Identifier,
		NextProtos: []string{acme.ACMETLS1Protocol},
	})
	if problem != nil {
		result.Error = problem
		return result
	}

	leafCert := certs[0]

	// Verify SNI
	if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], task.Identifier) {
		names := certNames(leafCert)
		errText := fmt.Sprintf(
			"Incorrect validation certificate for %s challenge. "+
				"Requested %s from %s. Received %d certificate(s), "+
				"first certificate had names %q",
			acme.ChallengeTLSALPN01, task.Identifier, hostPort, len(certs), names)
		result.Error = acme.UnauthorizedProblem(errText)
		return result
	}

	// Verify key authorization in acmeValidation extension
	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	h := sha256.Sum256([]byte(expectedKeyAuthorization))
	for _, ext := range leafCert.Extensions {
		if IdPeAcmeIdentifierV1.Equal(ext.Id) && ext.Critical {
			if subtle.ConstantTimeCompare(h[:], ext.Value) == 1 {
				return result
			}
			result.Error = acme.UnauthorizedProblem("Extension acmeValidationV1 value incorrect.")
			return result
		}
	}

	return result
}

func (va VAImpl) fetchCerts(hostPort string, config *tls.Config) ([]*x509.Certificate, *acme.ProblemDetails) {
	config = config.Clone()
	config.InsecureSkipVerify = true
	conn, err := tls.DialWithDialer(&net.Dialer{Timeout: time.Second * 5}, "tcp", hostPort, config)

	if err != nil {
		// TODO(@cpu): Return better err - see parseHTTPConnError from boulder
		return nil, acme.UnauthorizedProblem(
			fmt.Sprintf("Failed to connect to %s for the %s challenge", hostPort, acme.ChallengeTLSALPN01))
	}

	// close errors are not important here
	defer func() {
		_ = conn.Close()
	}()

	certs := conn.ConnectionState().PeerCertificates
	if len(certs) == 0 {
		return nil, acme.UnauthorizedProblem(
			fmt.Sprintf("No certs presented for %s challenge", acme.ChallengeTLSALPN01))
	}
	return certs, nil
}

func (va VAImpl) validateHTTP01(task *vaTask) *core.ValidationRecord {
	body, url, err := va.fetchHTTP(task.Identifier, task.Challenge.Token)

	result := &core.ValidationRecord{
		URL:         url,
		ValidatedAt: va.clk.Now(),
		Error:       err,
	}
	if result.Error != nil {
		return result
	}

	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	// The server SHOULD ignore whitespace characters at the end of the body
	payload := strings.TrimRight(string(body), whitespaceCutset)
	if payload != expectedKeyAuthorization {
		result.Error = acme.UnauthorizedProblem(
			fmt.Sprintf("The key authorization file from the server did not match this challenge %q != %q",
				expectedKeyAuthorization, payload))
	}

	return result
}

// NOTE(@cpu): fetchHTTP only fetches the ACME HTTP-01 challenge path for
// a given challenge & identifier domain. It is not a challenge agnostic general
// purpose HTTP function
func (va VAImpl) fetchHTTP(identifier string, token string) ([]byte, string, *acme.ProblemDetails) {
	path := fmt.Sprintf("%s%s", acme.HTTP01BaseURL, token)

	url := &url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("%s:%d", identifier, va.httpPort),
		Path:   path,
	}

	va.log.Printf("Attempting to validate w/ HTTP: %s\n", url)
	httpRequest, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, url.String(), acme.MalformedProblem(
			fmt.Sprintf("Invalid URL %q\n", url.String()))
	}
	httpRequest.Header.Set("User-Agent", userAgent())
	httpRequest.Header.Set("Accept", "*/*")

	transport := &http.Transport{
		// We don't expect to make multiple requests to a client, so close
		// connection immediately.
		DisableKeepAlives: true,
	}

	client := &http.Client{
		Transport: transport,
		Timeout:   time.Second * 5,
	}

	resp, err := client.Do(httpRequest)
	if err != nil {
		return nil, url.String(), acme.ConnectionProblem(err.Error())
	}

	// NOTE: This is *not* using a `io.LimitedReader` and isn't suitable for
	// production because a very large response will bog down the server. Don't
	// use Pebble anywhere that isn't a testing rig!!!
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, url.String(), acme.InternalErrorProblem(err.Error())
	}
	err = resp.Body.Close()
	if err != nil {
		return nil, url.String(), acme.InternalErrorProblem(err.Error())
	}

	if resp.StatusCode != 200 {
		return nil, url.String(), acme.UnauthorizedProblem(
			fmt.Sprintf("Non-200 status code from HTTP: %s returned %d",
				url.String(), resp.StatusCode))
	}

	return body, url.String(), nil
}
<commit_msg>Review fixes.<commit_after>package va

import (
	"crypto/sha256"
	"crypto/subtle"
	"crypto/tls"
	"crypto/x509"
	"encoding/asn1"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/jmhodges/clock"
	"github.com/letsencrypt/pebble/acme"
	"github.com/letsencrypt/pebble/core"
)

const (
	whitespaceCutset = "\n\r\t"
	userAgentBase    = "LetsEncrypt-Pebble-VA"

	// How long do valid authorizations last before expiring?
	validAuthzExpire = time.Hour

	// How many vaTasks can be in the channel before the WFE blocks on adding
	// another?
	taskQueueSize = 6

	// How many concurrent validations are performed?
	concurrentValidations = 3

	// noSleepEnvVar defines the environment variable name used to signal that the
	// VA should *not* sleep between validation attempts. Set this to 1 when you
	// invoke Pebble if you wish validation to be done at full speed, e.g.:
	//   PEBBLE_VA_NOSLEEP=1 pebble
	noSleepEnvVar = "PEBBLE_VA_NOSLEEP"
)

var IdPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}

func userAgent() string {
	return fmt.Sprintf(
		"%s (%s; %s)",
		userAgentBase, runtime.GOOS, runtime.GOARCH)
}

// certNames collects up all of a certificate's subject names (Subject CN and
// Subject Alternate Names) and reduces them to a comma joined string.
func certNames(cert *x509.Certificate) string {
	var names []string
	if cert.Subject.CommonName != "" {
		names = append(names, cert.Subject.CommonName)
	}
	names = append(names, cert.DNSNames...)
	return strings.Join(names, ", ")
}

type vaTask struct {
	Identifier string
	Challenge  *core.Challenge
	Account    *core.Account
}

type VAImpl struct {
	log      *log.Logger
	clk      clock.Clock
	httpPort int
	tlsPort  int
	tasks    chan *vaTask
	sleep    bool
}

func New(
	log *log.Logger,
	clk clock.Clock,
	httpPort, tlsPort int) *VAImpl {
	va := &VAImpl{
		log:      log,
		clk:      clk,
		httpPort: httpPort,
		tlsPort:  tlsPort,
		tasks:    make(chan *vaTask, taskQueueSize),
		sleep:    true,
	}

	// Read the PEBBLE_VA_NOSLEEP environment variable string
	noSleep := os.Getenv(noSleepEnvVar)
	// If it is set to something true-like, then the VA shouldn't sleep
	switch noSleep {
	case "1", "true", "True", "TRUE":
		va.sleep = false
		va.log.Printf("Disabling random VA sleeps")
	}

	go va.processTasks()
	return va
}

func (va VAImpl) ValidateChallenge(ident string, chal *core.Challenge, acct *core.Account) {
	task := &vaTask{
		Identifier: ident,
		Challenge:  chal,
		Account:    acct,
	}
	// Submit the task for validation
	va.tasks <- task
}

func (va VAImpl) processTasks() {
	for task := range va.tasks {
		go va.process(task)
	}
}

func (va VAImpl) firstError(results chan *core.ValidationRecord) *acme.ProblemDetails {
	for i := 0; i < concurrentValidations; i++ {
		result := <-results
		if result.Error != nil {
			return result.Error
		}
	}
	return nil
}

func (va VAImpl) process(task *vaTask) {
	va.log.Printf("Pulled a task from the Tasks queue: %#v", task)
	va.log.Printf("Starting %d validations.", concurrentValidations)

	chal := task.Challenge
	chal.Lock()
	// Update the validated date for the challenge
	now := va.clk.Now().UTC()
	chal.ValidatedDate = now
	chal.Validated = chal.ValidatedDate.Format(time.RFC3339)
	authz := chal.Authz
	chal.Unlock()

	results := make(chan *core.ValidationRecord, concurrentValidations)

	// Start a number of go routines to perform concurrent validations
	for i := 0; i < concurrentValidations; i++ {
		go va.performValidation(task, results)
	}

	err := va.firstError(results)
	// If one of the results was an error, the challenge fails
	if err != nil {
		// Lock the challenge to update the error & status
		chal.Lock()
		// Update the challenge Error
		chal.Error = err
		// Set the challenge and authorization to invalid
		chal.Status = acme.StatusInvalid
		chal.Unlock()

		// Lock the authz to update the authz status
		authz.Lock()
		authz.Status = acme.StatusInvalid
		authz.Unlock()

		va.log.Printf("authz %s set INVALID by completed challenge %s", authz.ID, chal.ID)
		// Return immediately - there's no need to check for order issuance
		return
	} else {
		// If none of the results were an error then the challenge succeeded.
		// Update the expiry for the valid authorization
		authz.Lock()
		authz.ExpiresDate = now.Add(validAuthzExpire)
		authz.Expires = authz.ExpiresDate.Format(time.RFC3339)
		authz.Status = acme.StatusValid
		authz.Unlock()

		// Set the authorization & challenge to valid
		chal.Lock()
		chal.Status = acme.StatusValid
		chal.Unlock()

		va.log.Printf("authz %s set VALID by completed challenge %s", authz.ID, chal.ID)
	}
}

func (va VAImpl) performValidation(task *vaTask, results chan<- *core.ValidationRecord) {
	if va.sleep {
		// Sleep for a random amount of time between 1-15s
		len := time.Duration(rand.Intn(15))
		va.log.Printf("Sleeping for %s seconds before validating", time.Second*len)
		va.clk.Sleep(time.Second * len)
	}

	switch task.Challenge.Type {
	case acme.ChallengeHTTP01:
		results <- va.validateHTTP01(task)
	case acme.ChallengeTLSALPN01:
		results <- va.validateTLSALPN01(task)
	case acme.ChallengeDNS01:
		results <- va.validateDNS01(task)
	default:
		va.log.Printf("Error: performValidation(): Invalid challenge type: %q", task.Challenge.Type)
	}
}

func (va VAImpl) validateDNS01(task *vaTask) *core.ValidationRecord {
	const dns01Prefix = "_acme-challenge"
	challengeSubdomain := fmt.Sprintf("%s.%s", dns01Prefix, task.Identifier)

	result := &core.ValidationRecord{
		URL:         challengeSubdomain,
		ValidatedAt: va.clk.Now(),
	}

	txts, err := net.LookupTXT(challengeSubdomain)
	if err != nil {
		result.Error = acme.UnauthorizedProblem("Error retrieving TXT records for DNS challenge")
		return result
	}

	if len(txts) == 0 {
		msg := fmt.Sprintf("No TXT records found for DNS challenge")
		result.Error = acme.UnauthorizedProblem(msg)
		return result
	}

	task.Challenge.RLock()
	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	h := sha256.Sum256([]byte(expectedKeyAuthorization))
	task.Challenge.RUnlock()
	authorizedKeysDigest := base64.RawURLEncoding.EncodeToString(h[:])

	for _, element := range txts {
		if subtle.ConstantTimeCompare([]byte(element), []byte(authorizedKeysDigest)) == 1 {
			return result
		}
	}

	msg := fmt.Sprintf("Correct value not found for DNS challenge")
	result.Error = acme.UnauthorizedProblem(msg)
	return result
}

func (va VAImpl) validateTLSALPN01(task *vaTask) *core.ValidationRecord {
	portString := strconv.Itoa(va.tlsPort)
	hostPort := net.JoinHostPort(task.Identifier, portString)

	result := &core.ValidationRecord{
		URL:         hostPort,
		ValidatedAt: va.clk.Now(),
	}

	cs, problem := va.fetchConnectionState(hostPort, &tls.Config{
		ServerName:         task.Identifier,
		NextProtos:         []string{acme.ACMETLS1Protocol},
		InsecureSkipVerify: true,
	})
	if problem != nil {
		result.Error = problem
		return result
	}

	if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != acme.ACMETLS1Protocol {
		result.Error = acme.UnauthorizedProblem(fmt.Sprintf(
			"Cannot connect using %s for %s challenge",
			acme.ACMETLS1Protocol,
			acme.ChallengeTLSALPN01,
		))
		return result
	}

	certs := cs.PeerCertificates
	if len(certs) == 0 {
		result.Error = acme.UnauthorizedProblem(fmt.Sprintf("No certs presented for %s challenge", acme.ChallengeTLSALPN01))
		return result
	}
	leafCert := certs[0]

	// Verify SNI
	if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], task.Identifier) {
		names := certNames(leafCert)
		errText := fmt.Sprintf(
			"Incorrect validation certificate for %s challenge. "+
				"Requested %s from %s. Received %d certificate(s), "+
				"first certificate had names %q",
			acme.ChallengeTLSALPN01, task.Identifier, hostPort, len(certs), names)
		result.Error = acme.UnauthorizedProblem(errText)
		return result
	}

	// Verify key authorization in acmeValidation extension
	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	h := sha256.Sum256([]byte(expectedKeyAuthorization))
	for _, ext := range leafCert.Extensions {
		if IdPeAcmeIdentifierV1.Equal(ext.Id) && ext.Critical {
			if subtle.ConstantTimeCompare(h[:], ext.Value) == 1 {
				return result
			}
			result.Error = acme.UnauthorizedProblem("Extension acmeValidationV1 value incorrect.")
			return result
		}
	}

	return result
}

func (va VAImpl) fetchConnectionState(hostPort string, config *tls.Config) (*tls.ConnectionState, *acme.ProblemDetails) {
	conn, err := tls.DialWithDialer(&net.Dialer{Timeout: time.Second * 5}, "tcp", hostPort, config)

	if err != nil {
		// TODO(@cpu): Return better err - see parseHTTPConnError from boulder
		return nil, acme.UnauthorizedProblem(
			fmt.Sprintf("Failed to connect to %s for the %s challenge", hostPort, acme.ChallengeTLSALPN01))
	}

	// close errors are not important here
	defer func() {
		_ = conn.Close()
	}()

	cs := conn.ConnectionState()
	return &cs, nil
}

func (va VAImpl) validateHTTP01(task *vaTask) *core.ValidationRecord {
	body, url, err := va.fetchHTTP(task.Identifier, task.Challenge.Token)

	result := &core.ValidationRecord{
		URL:         url,
		ValidatedAt: va.clk.Now(),
		Error:       err,
	}
	if result.Error != nil {
		return result
	}

	expectedKeyAuthorization := task.Challenge.ExpectedKeyAuthorization(task.Account.Key)
	// The server SHOULD ignore whitespace characters at the end of the body
	payload := strings.TrimRight(string(body), whitespaceCutset)
	if payload != expectedKeyAuthorization {
		result.Error = acme.UnauthorizedProblem(
			fmt.Sprintf("The key authorization file from the server did not match this challenge %q != %q",
				expectedKeyAuthorization, payload))
	}

	return result
}

// NOTE(@cpu): fetchHTTP only fetches the ACME HTTP-01 challenge path for
// a given challenge & identifier domain. It is not a challenge agnostic general
// purpose HTTP function
func (va VAImpl) fetchHTTP(identifier string, token string) ([]byte, string, *acme.ProblemDetails) {
	path := fmt.Sprintf("%s%s", acme.HTTP01BaseURL, token)

	url := &url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("%s:%d", identifier, va.httpPort),
		Path:   path,
	}

	va.log.Printf("Attempting to validate w/ HTTP: %s\n", url)
	httpRequest, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, url.String(), acme.MalformedProblem(
			fmt.Sprintf("Invalid URL %q\n", url.String()))
	}
	httpRequest.Header.Set("User-Agent", userAgent())
	httpRequest.Header.Set("Accept", "*/*")

	transport := &http.Transport{
		// We don't expect to make multiple requests to a client, so close
		// connection immediately.
		DisableKeepAlives: true,
	}

	client := &http.Client{
		Transport: transport,
		Timeout:   time.Second * 5,
	}

	resp, err := client.Do(httpRequest)
	if err != nil {
		return nil, url.String(), acme.ConnectionProblem(err.Error())
	}

	// NOTE: This is *not* using a `io.LimitedReader` and isn't suitable for
	// production because a very large response will bog down the server. Don't
	// use Pebble anywhere that isn't a testing rig!!!
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, url.String(), acme.InternalErrorProblem(err.Error())
	}
	err = resp.Body.Close()
	if err != nil {
		return nil, url.String(), acme.InternalErrorProblem(err.Error())
	}

	if resp.StatusCode != 200 {
		return nil, url.String(), acme.UnauthorizedProblem(
			fmt.Sprintf("Non-200 status code from HTTP: %s returned %d",
				url.String(), resp.StatusCode))
	}

	return body, url.String(), nil
}
<|endoftext|>


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

package dparval

import (
	"fmt"
	"strconv"

	jsonpointer "github.com/dustin/go-jsonpointer"
	json "github.com/dustin/gojson"
)

const (
	NOT_JSON = iota
	NULL
	BOOLEAN
	NUMBER
	STRING
	ARRAY
	OBJECT
)

type Undefined struct {
	Path string
}

func (this *Undefined) Error() string {
	if this.Path != "" {
		return fmt.Sprintf("%s is not defined", this.Path)
	}
	return fmt.Sprint("not defined")
}

type ValueChannel chan Value
type ValueCollection []Value

type Value interface {
	Path(path string) (Value, error)
	SetPath(path string, val interface{})
	Index(index int) (Value, error)
	SetIndex(index int, val interface{})
	Type() int
	Value() (interface{}, error)
	AddMeta(key string, val interface{})
	Meta() Value
}

type value struct {
	raw         []byte
	parsedValue interface{}
	alias       map[string]Value
	parsedType  int
	meta        Value
}

func NewValue(val interface{}) Value {
	switch val := val.(type) {
	case nil:
		return NewNullValue()
	case bool:
		return NewBooleanValue(val)
	case float64:
		return NewNumberValue(val)
	case string:
		return NewStringValue(val)
	case []interface{}:
		return NewArrayValue(val)
	case map[string]interface{}:
		return NewObjectValue(val)
	default:
		panic(fmt.Sprintf("Cannot create value for type %T", val))
	}
}

func NewNullValue() Value {
	rv := value{
		parsedType: NULL,
	}
	return &rv
}

func NewBooleanValue(val bool) Value {
	rv := value{
		parsedType:  BOOLEAN,
		parsedValue: val,
	}
	return &rv
}

func NewNumberValue(val float64) Value {
	rv := value{
		parsedType:  NUMBER,
		parsedValue: val,
	}
	return &rv
}

func NewStringValue(val string) Value {
	rv := value{
		parsedType:  STRING,
		parsedValue: val,
	}
	return &rv
}

func NewArrayValue(val []interface{}) Value {
	rv := value{
		parsedType: ARRAY,
	}

	parsedValue := make([]Value, len(val))
	for i, v := range val {
		switch v := v.(type) {
		case Value:
			parsedValue[i] = v
		default:
			parsedValue[i] = NewValue(v)
		}
	}
	rv.parsedValue = parsedValue

	return &rv
}

func NewEmptyObjectValue() Value {
	return NewObjectValue(map[string]interface{}{})
}

func NewObjectValue(val map[string]interface{}) Value {
	rv := value{
		parsedType: OBJECT,
	}

	parsedValue := make(map[string]Value)
	for k, v := range val {
		switch v := v.(type) {
		case Value:
			parsedValue[k] = v
		default:
			parsedValue[k] = NewValue(v)
		}
	}
	rv.parsedValue = parsedValue

	return &rv
}

func NewValueFromBytes(bytes []byte) Value {
	rv := value{
		raw:         bytes,
		parsedType:  -1,
		parsedValue: nil,
		alias:       nil,
	}
	err := json.Validate(bytes)
	if err != nil {
		rv.parsedType = NOT_JSON
	} else {
		rv.parsedType = identifyType(bytes)
	}
	return &rv
}

func identifyType(bytes []byte) int {
	for _, b := range bytes {
		switch b {
		case '{':
			return OBJECT
		case '[':
			return ARRAY
		case '"':
			return STRING
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			return NUMBER
		case 't', 'f':
			return BOOLEAN
		case 'n':
			return NULL
		}
	}
	panic("Unable to identify type of valid JSON")
	return -1
}

func (this *value) Path(path string) (Value, error) {
	// aliases always have priority

	if this.alias != nil {
		result, ok := this.alias[path]
		if ok {
			return result, nil
		}
	}
	// next we already parsed, used that
	switch parsedValue := this.parsedValue.(type) {
	case map[string]Value:
		result, ok := parsedValue[path]
		if ok {
			return result, nil
		}
	}
	// finally, consult the raw bytes
	if this.raw != nil {
		res, err := jsonpointer.Find(this.raw, "/"+path)
		if err != nil {
			return nil, err
		}
		if res != nil {
			return NewValueFromBytes(res), nil
		}
	}

	return nil, &Undefined{path}
}

func (this *value) SetPath(path string, val interface{}) {

	if this.parsedType == OBJECT {
		switch parsedValue := this.parsedValue.(type) {
		case map[string]Value:
			// if we've already parsed the object, store it there
			switch val := val.(type) {
			case Value:
				parsedValue[path] = val
			default:
				parsedValue[path] = NewValue(val)
			}
		case nil:
			// if not store it in alias
			if this.alias == nil {
				this.alias = make(map[string]Value)
			}
			switch val := val.(type) {
			case Value:
				this.alias[path] = val
			default:
				this.alias[path] = NewValue(val)
			}

		}
	}
}

func (this *value) Index(index int) (Value, error) {
	// aliases always have priority
	if this.alias != nil {
		result, ok := this.alias[strconv.Itoa(index)]
		if ok {
			return result, nil
		}
	}
	// next we already parsed, used that
	switch parsedValue := this.parsedValue.(type) {
	case []Value:
		if index >= 0 && index < len(parsedValue) {
			result := parsedValue[index]
			return result, nil
		} else {
			// this way it behaves consistent with jsonpointer below
			return nil, &Undefined{}
		}
	}
	// finally, consult the raw bytes
	if this.raw != nil {
		res, err := jsonpointer.Find(this.raw, "/"+strconv.Itoa(index))
		if err != nil {
			return nil, err
		}
		if res != nil {
			return NewValueFromBytes(res), nil
		}
	}

	return nil, &Undefined{}
}

func (this *value) SetIndex(index int, val interface{}) {
	if this.parsedType == ARRAY && index >= 0 {
		switch parsedValue := this.parsedValue.(type) {
		case []Value:
			if index < len(parsedValue) {
				// if we've already parsed the object, store it there
				switch val := val.(type) {
				case Value:
					parsedValue[index] = val
				default:
					parsedValue[index] = NewValue(val)
				}
			}
		case nil:
			// if not store it in alias
			if this.alias == nil {
				this.alias = make(map[string]Value)
			}
			switch val := val.(type) {
			case Value:
				this.alias[strconv.Itoa(index)] = val
			default:
				this.alias[strconv.Itoa(index)] = NewValue(val)
			}

		}
	}
}

func (this *value) Type() int {
	return this.parsedType
}

func (this *value) AddMeta(key string, val interface{}) {
	if this.meta == nil {
		this.meta = NewEmptyObjectValue()
	}
	this.meta.SetPath(key, val)
}

func (this *value) Meta() Value {
	return this.meta
}

// the Value function exits this type system
// it can be the most expensive call, as it may force parsing
// large amounts of previously unparsed json
func (this *value) Value() (interface{}, error) {
	if this.parsedValue != nil || this.parsedType == NULL {
		rv, err := devalue(this.parsedValue)
		if err != nil {
			return nil, err
		}
		if this.alias != nil {
			err := overlayAlias(rv, this.alias)
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	} else if this.parsedType != NOT_JSON {
		err := json.Unmarshal(this.raw, &this.parsedValue)
		if err != nil {
			panic("unexpected parse error on valid JSON")
		}
		// if there are any aliases, we must make a safe copy
		// and then overlay them
		if this.alias != nil {
			// we cannot damange the original parsed value
			rv, err := safeCopy(this.parsedValue)
			if err != nil {
				return nil, err
			}
			err = overlayAlias(rv, this.alias)
			if err != nil {
				return nil, err
			}
			return rv, nil
		} else {
			// otherwise its safe to return directly
			return this.parsedValue, nil
		}
	} else {
		return nil, &Undefined{}
	}
}

func devalue(base interface{}) (interface{}, error) {
	var err error
	switch base := base.(type) {
	case map[string]Value:
		rv := make(map[string]interface{}, len(base))
		for k, v := range base {
			rv[k], err = v.Value()
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	case []Value:
		rv := make([]interface{}, len(base))
		for i, v := range base {
			rv[i], err = v.Value()
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	default:
		return base, nil
	}
}

func safeCopy(base interface{}) (interface{}, error) {
	switch base := base.(type) {
	case map[string]interface{}:
		rv := make(map[string]interface{}, len(base))
		for k, v := range base {
			rv[k] = v
		}
		return rv, nil
	case []interface{}:
		rv := make([]interface{}, len(base))
		for i, v := range base {
			rv[i] = v
		}
		return rv, nil
	default:
		return base, nil
	}
}

func overlayAlias(base interface{}, alias map[string]Value) error {
	var err error
	switch base := base.(type) {
	case map[string]interface{}:
		for k, v := range alias {
			base[k], err = v.Value()
			if err != nil {
				return err
			}
		}
	case []interface{}:
		for k, v := range alias {
			bigi, err := strconv.ParseInt(k, 10, 32)
			if err != nil {
				return err
			}
			i := int(bigi)
			if i >= 0 && i < len(base) {
				base[i], err = v.Value()
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<commit_msg>NewValue on existin Value is allowed, and preserves original<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package dparval

import (
	"fmt"
	"strconv"

	jsonpointer "github.com/dustin/go-jsonpointer"
	json "github.com/dustin/gojson"
)

const (
	NOT_JSON = iota
	NULL
	BOOLEAN
	NUMBER
	STRING
	ARRAY
	OBJECT
)

type Undefined struct {
	Path string
}

func (this *Undefined) Error() string {
	if this.Path != "" {
		return fmt.Sprintf("%s is not defined", this.Path)
	}
	return fmt.Sprint("not defined")
}

type ValueChannel chan Value
type ValueCollection []Value

type Value interface {
	Path(path string) (Value, error)
	SetPath(path string, val interface{})
	Index(index int) (Value, error)
	SetIndex(index int, val interface{})
	Type() int
	Value() (interface{}, error)

	AddMeta(key string, val interface{})
	Meta() Value
}

type value struct {
	raw         []byte
	parsedValue interface{}
	alias       map[string]Value
	parsedType  int
	meta        Value
}

func NewValue(val interface{}) Value {
	switch val := val.(type) {
	case nil:
		return NewNullValue()
	case bool:
		return NewBooleanValue(val)
	case float64:
		return NewNumberValue(val)
	case string:
		return NewStringValue(val)
	case []interface{}:
		return NewArrayValue(val)
	case map[string]interface{}:
		return NewObjectValue(val)
	case Value:
		return val
	default:
		panic(fmt.Sprintf("Cannot create value for type %T", val))
	}
}

func NewNullValue() Value {
	rv := value{
		parsedType: NULL,
	}
	return &rv
}

func NewBooleanValue(val bool) Value {
	rv := value{
		parsedType:  BOOLEAN,
		parsedValue: val,
	}
	return &rv
}

func NewNumberValue(val float64) Value {
	rv := value{
		parsedType:  NUMBER,
		parsedValue: val,
	}
	return &rv
}

func NewStringValue(val string) Value {
	rv := value{
		parsedType:  STRING,
		parsedValue: val,
	}
	return &rv
}

func NewArrayValue(val []interface{}) Value {
	rv := value{
		parsedType: ARRAY,
	}

	parsedValue := make([]Value, len(val))
	for i, v := range val {
		switch v := v.(type) {
		case Value:
			parsedValue[i] = v
		default:
			parsedValue[i] = NewValue(v)
		}
	}
	rv.parsedValue = parsedValue

	return &rv
}

func NewEmptyObjectValue() Value {
	return NewObjectValue(map[string]interface{}{})
}

func NewObjectValue(val map[string]interface{}) Value {
	rv := value{
		parsedType: OBJECT,
	}

	parsedValue := make(map[string]Value)
	for k, v := range val {
		switch v := v.(type) {
		case Value:
			parsedValue[k] = v
		default:
			parsedValue[k] = NewValue(v)
		}
	}
	rv.parsedValue = parsedValue

	return &rv
}

func NewValueFromBytes(bytes []byte) Value {
	rv := value{
		raw:         bytes,
		parsedType:  -1,
		parsedValue: nil,
		alias:       nil,
	}
	err := json.Validate(bytes)
	if err != nil {
		rv.parsedType = NOT_JSON
	} else {
		rv.parsedType = identifyType(bytes)
	}
	return &rv
}

func identifyType(bytes []byte) int {
	for _, b := range bytes {
		switch b {
		case '{':
			return OBJECT
		case '[':
			return ARRAY
		case '"':
			return STRING
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			return NUMBER
		case 't', 'f':
			return BOOLEAN
		case 'n':
			return NULL
		}
	}
	panic("Unable to identify type of valid JSON")
	return -1
}

func (this *value) Path(path string) (Value, error) {
	// aliases always have priority

	if this.alias != nil {
		result, ok := this.alias[path]
		if ok {
			return result, nil
		}
	}
	// next we already parsed, used that
	switch parsedValue := this.parsedValue.(type) {
	case map[string]Value:
		result, ok := parsedValue[path]
		if ok {
			return result, nil
		}
	}
	// finally, consult the raw bytes
	if this.raw != nil {
		res, err := jsonpointer.Find(this.raw, "/"+path)
		if err != nil {
			return nil, err
		}
		if res != nil {
			return NewValueFromBytes(res), nil
		}
	}

	return nil, &Undefined{path}
}

func (this *value) SetPath(path string, val interface{}) {

	if this.parsedType == OBJECT {
		switch parsedValue := this.parsedValue.(type) {
		case map[string]Value:
			// if we've already parsed the object, store it there
			switch val := val.(type) {
			case Value:
				parsedValue[path] = val
			default:
				parsedValue[path] = NewValue(val)
			}
		case nil:
			// if not store it in alias
			if this.alias == nil {
				this.alias = make(map[string]Value)
			}
			switch val := val.(type) {
			case Value:
				this.alias[path] = val
			default:
				this.alias[path] = NewValue(val)
			}

		}
	}
}

func (this *value) Index(index int) (Value, error) {
	// aliases always have priority
	if this.alias != nil {
		result, ok := this.alias[strconv.Itoa(index)]
		if ok {
			return result, nil
		}
	}
	// next we already parsed, used that
	switch parsedValue := this.parsedValue.(type) {
	case []Value:
		if index >= 0 && index < len(parsedValue) {
			result := parsedValue[index]
			return result, nil
		} else {
			// this way it behaves consistent with jsonpointer below
			return nil, &Undefined{}
		}
	}
	// finally, consult the raw bytes
	if this.raw != nil {
		res, err := jsonpointer.Find(this.raw, "/"+strconv.Itoa(index))
		if err != nil {
			return nil, err
		}
		if res != nil {
			return NewValueFromBytes(res), nil
		}
	}

	return nil, &Undefined{}
}

func (this *value) SetIndex(index int, val interface{}) {
	if this.parsedType == ARRAY && index >= 0 {
		switch parsedValue := this.parsedValue.(type) {
		case []Value:
			if index < len(parsedValue) {
				// if we've already parsed the object, store it there
				switch val := val.(type) {
				case Value:
					parsedValue[index] = val
				default:
					parsedValue[index] = NewValue(val)
				}
			}
		case nil:
			// if not store it in alias
			if this.alias == nil {
				this.alias = make(map[string]Value)
			}
			switch val := val.(type) {
			case Value:
				this.alias[strconv.Itoa(index)] = val
			default:
				this.alias[strconv.Itoa(index)] = NewValue(val)
			}

		}
	}
}

func (this *value) Type() int {
	return this.parsedType
}

func (this *value) AddMeta(key string, val interface{}) {
	if this.meta == nil {
		this.meta = NewEmptyObjectValue()
	}
	this.meta.SetPath(key, val)
}

func (this *value) Meta() Value {
	return this.meta
}

// the Value function exits this type system
// it can be the most expensive call, as it may force parsing
// large amounts of previously unparsed json
func (this *value) Value() (interface{}, error) {
	if this.parsedValue != nil || this.parsedType == NULL {
		rv, err := devalue(this.parsedValue)
		if err != nil {
			return nil, err
		}
		if this.alias != nil {
			err := overlayAlias(rv, this.alias)
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	} else if this.parsedType != NOT_JSON {
		err := json.Unmarshal(this.raw, &this.parsedValue)
		if err != nil {
			panic("unexpected parse error on valid JSON")
		}
		// if there are any aliases, we must make a safe copy
		// and then overlay them
		if this.alias != nil {
			// we cannot damange the original parsed value
			rv, err := safeCopy(this.parsedValue)
			if err != nil {
				return nil, err
			}
			err = overlayAlias(rv, this.alias)
			if err != nil {
				return nil, err
			}
			return rv, nil
		} else {
			// otherwise its safe to return directly
			return this.parsedValue, nil
		}
	} else {
		return nil, &Undefined{}
	}
}

func devalue(base interface{}) (interface{}, error) {
	var err error
	switch base := base.(type) {
	case map[string]Value:
		rv := make(map[string]interface{}, len(base))
		for k, v := range base {
			rv[k], err = v.Value()
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	case []Value:
		rv := make([]interface{}, len(base))
		for i, v := range base {
			rv[i], err = v.Value()
			if err != nil {
				return nil, err
			}
		}
		return rv, nil
	default:
		return base, nil
	}
}

func safeCopy(base interface{}) (interface{}, error) {
	switch base := base.(type) {
	case map[string]interface{}:
		rv := make(map[string]interface{}, len(base))
		for k, v := range base {
			rv[k] = v
		}
		return rv, nil
	case []interface{}:
		rv := make([]interface{}, len(base))
		for i, v := range base {
			rv[i] = v
		}
		return rv, nil
	default:
		return base, nil
	}
}

func overlayAlias(base interface{}, alias map[string]Value) error {
	var err error
	switch base := base.(type) {
	case map[string]interface{}:
		for k, v := range alias {
			base[k], err = v.Value()
			if err != nil {
				return err
			}
		}
	case []interface{}:
		for k, v := range alias {
			bigi, err := strconv.ParseInt(k, 10, 32)
			if err != nil {
				return err
			}
			i := int(bigi)
			if i >= 0 && i < len(base) {
				base[i], err = v.Value()
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package pongo2

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type Value struct {
	v reflect.Value
}

// Converts any given value to a pongo2.Value
// Usually being used within own functions passed to a template
// through a Context or within filter functions.
//
// Example:
//     AsValue("my string")
func AsValue(i interface{}) *Value {
	return &Value{
		v: reflect.ValueOf(i),
	}
}

func (v *Value) getResolvedValue() reflect.Value {
	if v.v.IsValid() && v.v.Kind() == reflect.Ptr {
		return v.v.Elem()
	}
	return v.v
}

// Checks whether the underlying value is a string
func (v *Value) IsString() bool {
	return v.getResolvedValue().Kind() == reflect.String
}

// Checks whether the underlying value is a bool
func (v *Value) IsBool() bool {
	return v.getResolvedValue().Kind() == reflect.Bool
}

// Checks whether the underlying value is a float
func (v *Value) IsFloat() bool {
	return v.getResolvedValue().Kind() == reflect.Float32 ||
		v.getResolvedValue().Kind() == reflect.Float64
}

// Checks whether the underlying value is an integer
func (v *Value) IsInteger() bool {
	return v.getResolvedValue().Kind() == reflect.Int ||
		v.getResolvedValue().Kind() == reflect.Int8 ||
		v.getResolvedValue().Kind() == reflect.Int16 ||
		v.getResolvedValue().Kind() == reflect.Int32 ||
		v.getResolvedValue().Kind() == reflect.Int64 ||
		v.getResolvedValue().Kind() == reflect.Uint ||
		v.getResolvedValue().Kind() == reflect.Uint8 ||
		v.getResolvedValue().Kind() == reflect.Uint16 ||
		v.getResolvedValue().Kind() == reflect.Uint32 ||
		v.getResolvedValue().Kind() == reflect.Uint64
}

// Checks whether the underlying value is either an integer
// or a float.
func (v *Value) IsNumber() bool {
	return v.IsInteger() || v.IsFloat()
}

// Checks whether the underlying value is NIL
func (v *Value) IsNil() bool {
	//fmt.Printf("%+v\n", v.getResolvedValue().Type().String())
	return !v.getResolvedValue().IsValid()
}

// Returns a string for the underlying value. If this value is not
// of type string, pongo2 tries to convert it. Currently the following
// types for underlying values are supported:
//
//     1. string
//     2. int/uint (any size)
//     3. float (any precision)
//     4. bool
//     5. time.Time
//
// NIL values will lead to an empty string. Unsupported types are leading
// to their respective type name.
func (v *Value) String() string {
	if v.IsNil() {
		return ""
	}

	switch v.getResolvedValue().Kind() {
	case reflect.String:
		return v.getResolvedValue().String()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.getResolvedValue().Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(v.getResolvedValue().Uint(), 10)
	case reflect.Float32, reflect.Float64:
		return fmt.Sprintf("%f", v.getResolvedValue().Float())
	case reflect.Bool:
		if v.Bool() {
			return "True"
		} else {
			return "False"
		}
	case reflect.Struct:
		if v.getResolvedValue().Type() == reflect.TypeOf(time.Time{}) {
			return v.getResolvedValue().Interface().(time.Time).String()
		}
		if t, ok := v.Interface().(fmt.Stringer); ok {
			return t.String()
		}
	}

	logf("Value.String() not implemented for type: %s\n", v.getResolvedValue().Kind().String())
	return v.getResolvedValue().String()
}

// Returns the underlying value as an integer (converts the underlying
// value, if necessary). If it's not possible to convert the underlying value,
// it will return 0.
func (v *Value) Integer() int {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return int(v.getResolvedValue().Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return int(v.getResolvedValue().Uint())
	case reflect.Float32, reflect.Float64:
		return int(v.getResolvedValue().Float())
	case reflect.String:
		// Try to convert from string to int (base 10)
		f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
		if err != nil {
			return 0
		}
		return int(f)
	default:
		logf("Value.Integer() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0
	}
}

// Returns the underlying value as a float (converts the underlying
// value, if necessary). If it's not possible to convert the underlying value,
// it will return 0.0.
func (v *Value) Float() float64 {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return float64(v.getResolvedValue().Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return float64(v.getResolvedValue().Uint())
	case reflect.Float32, reflect.Float64:
		return v.getResolvedValue().Float()
	case reflect.String:
		// Try to convert from string to float64 (base 10)
		f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
		if err != nil {
			return 0.0
		}
		return f
	default:
		logf("Value.Float() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0.0
	}
}

// Returns the underlying value as bool. If the value is not bool, false
// will always be returned. If you're looking for true/false-evaluation of the
// underlying value, have a look on the IsTrue()-function.
func (v *Value) Bool() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Bool:
		return v.getResolvedValue().Bool()
	default:
		logf("Value.Bool() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Tried to evaluate the underlying value the Pythonic-way:
//
// Returns TRUE in one the following cases:
//
//     * int != 0
//     * uint != 0
//     * float != 0.0
//     * len(array/chan/map/slice/string) > 0
//     * bool == true
//
// Otherwise returns always FALSE.
func (v *Value) IsTrue() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.getResolvedValue().Int() != 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return v.getResolvedValue().Uint() != 0
	case reflect.Float32, reflect.Float64:
		return v.getResolvedValue().Float() != 0
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.getResolvedValue().Len() > 0
	case reflect.Bool:
		return v.getResolvedValue().Bool()
	case reflect.Struct:
		return true // struct instance is always true
	default:
		logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Tries to negate the underlying value. It's mainly used for
// the NOT-operator and in conjunction with a call to
// return_value.IsTrue() afterwards.
//
// Example:
//     AsValue(1).Negate().IsTrue() == false
func (v *Value) Negate() *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v.Integer() != 0 {
			return AsValue(0)
		} else {
			return AsValue(1)
		}
	case reflect.Float32, reflect.Float64:
		if v.Float() != 0.0 {
			return AsValue(float64(0.0))
		} else {
			return AsValue(float64(1.1))
		}
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return AsValue(v.getResolvedValue().Len() == 0)
	case reflect.Bool:
		return AsValue(!v.getResolvedValue().Bool())
	default:
		logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue(true)
	}
}

// Returns the length for an array, chan, map, slice or string.
// Otherwise it will return 0.
func (v *Value) Len() int {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.getResolvedValue().Len()
	default:
		logf("Value.Len() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0
	}
}

// Slices an array, slice or string. Otherwise it will
// return an empty []int.
func (v *Value) Slice(i, j int) *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice, reflect.String:
		return AsValue(v.getResolvedValue().Slice(i, j).Interface())
	default:
		logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue([]int{})
	}
}

// Get the i-th item of an array, slice or string. Otherwise
// it will return NIL.
func (v *Value) Index(i int) *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice:
		if i >= v.Len() {
			return AsValue(nil)
		}
		return AsValue(v.getResolvedValue().Index(i).Interface())
	case reflect.String:
		return AsValue(v.getResolvedValue().Slice(i, i+1).Interface())
	default:
		logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue([]int{})
	}
}

// Checks whether the underlying value (which must be of type struct, map,
// string, array or slice) contains of another Value (e. g. used to check
// whether a struct contains of a specific field or a map contains a specific key).
//
// Example:
//     AsValue("Hello, World!").Contains(AsValue("World")) == true
func (v *Value) Contains(other *Value) bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Struct:
		field_value := v.getResolvedValue().FieldByName(other.String())
		return field_value.IsValid()
	case reflect.Map:
		var map_value reflect.Value
		switch other.Interface().(type) {
		case int:
			map_value = v.getResolvedValue().MapIndex(other.getResolvedValue())
		case string:
			map_value = v.getResolvedValue().MapIndex(other.getResolvedValue())
		default:
			logf("Value.Contains() does not support lookup type '%s'\n", other.getResolvedValue().Kind().String())
			return false
		}

		return map_value.IsValid()
	case reflect.String:
		return strings.Contains(v.getResolvedValue().String(), other.String())

	// TODO: reflect.Array, reflect.Slice

	default:
		logf("Value.Contains() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Checks whether the underlying value is of type array, slice or string.
// You normally would use CanSlice() before using the Slice() operation.
func (v *Value) CanSlice() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice, reflect.String:
		return true
	}
	return false
}

// Iterates over a map, array, slice or a string. It calls the
// function's first argument for every value with the following arguments:
//
//     idx      current 0-index
//     count    total amount of items
//     key      *Value for the key or item
//     value    *Value (only for maps, the respective value for a specific key)
//
// If the underlying value has no items or is not one of the types above,
// the empty function (function's second argument) will be called.
func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func()) {
	switch v.getResolvedValue().Kind() {
	case reflect.Map:
		keys := v.getResolvedValue().MapKeys()
		keyLen := len(keys)
		for idx, key := range keys {
			value := v.getResolvedValue().MapIndex(key)
			if !fn(idx, keyLen, &Value{key}, &Value{value}) {
				return
			}
		}
		if keyLen == 0 {
			empty()
		}
		return // done
	case reflect.Array, reflect.Slice:
		itemCount := v.getResolvedValue().Len()
		if itemCount > 0 {
			for i := 0; i < itemCount; i++ {
				if !fn(i, itemCount, &Value{v.getResolvedValue().Index(i)}, nil) {
					return
				}
			}
		} else {
			empty()
		}
		return // done
	case reflect.String:
		// TODO: Not utf8-compatible (utf8-decoding neccessary)
		charCount := v.getResolvedValue().Len()
		if charCount > 0 {
			for i := 0; i < charCount; i++ {
				if !fn(i, charCount, &Value{v.getResolvedValue().Slice(i, i+1)}, nil) {
					return
				}
			}
		} else {
			empty()
		}
		return // done
	default:
		logf("Value.Iterate() not available for type: %s\n", v.getResolvedValue().Kind().String())
	}
	empty()
}

// Gives you access to the underlying value.
func (v *Value) Interface() interface{} {
	if v.v.IsValid() {
		return v.v.Interface()
	}
	return nil
}

// Checks whether two values are containing the same value or object.
func (v *Value) EqualValueTo(other *Value) bool {
	return v.Interface() == other.Interface()
}
<commit_msg>Slight fixes to the docs; removed time.String() since it's only another Stringer-interface-implementation.<commit_after>package pongo2

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type Value struct {
	v reflect.Value
}

// Converts any given value to a pongo2.Value
// Usually being used within own functions passed to a template
// through a Context or within filter functions.
//
// Example:
//     AsValue("my string")
func AsValue(i interface{}) *Value {
	return &Value{
		v: reflect.ValueOf(i),
	}
}

func (v *Value) getResolvedValue() reflect.Value {
	if v.v.IsValid() && v.v.Kind() == reflect.Ptr {
		return v.v.Elem()
	}
	return v.v
}

// Checks whether the underlying value is a string
func (v *Value) IsString() bool {
	return v.getResolvedValue().Kind() == reflect.String
}

// Checks whether the underlying value is a bool
func (v *Value) IsBool() bool {
	return v.getResolvedValue().Kind() == reflect.Bool
}

// Checks whether the underlying value is a float
func (v *Value) IsFloat() bool {
	return v.getResolvedValue().Kind() == reflect.Float32 ||
		v.getResolvedValue().Kind() == reflect.Float64
}

// Checks whether the underlying value is an integer
func (v *Value) IsInteger() bool {
	return v.getResolvedValue().Kind() == reflect.Int ||
		v.getResolvedValue().Kind() == reflect.Int8 ||
		v.getResolvedValue().Kind() == reflect.Int16 ||
		v.getResolvedValue().Kind() == reflect.Int32 ||
		v.getResolvedValue().Kind() == reflect.Int64 ||
		v.getResolvedValue().Kind() == reflect.Uint ||
		v.getResolvedValue().Kind() == reflect.Uint8 ||
		v.getResolvedValue().Kind() == reflect.Uint16 ||
		v.getResolvedValue().Kind() == reflect.Uint32 ||
		v.getResolvedValue().Kind() == reflect.Uint64
}

// Checks whether the underlying value is either an integer
// or a float.
func (v *Value) IsNumber() bool {
	return v.IsInteger() || v.IsFloat()
}

// Checks whether the underlying value is NIL
func (v *Value) IsNil() bool {
	//fmt.Printf("%+v\n", v.getResolvedValue().Type().String())
	return !v.getResolvedValue().IsValid()
}

// Returns a string for the underlying value. If this value is not
// of type string, pongo2 tries to convert it. Currently the following
// types for underlying values are supported:
//
//     1. string
//     2. int/uint (any size)
//     3. float (any precision)
//     4. bool
//     5. time.Time
//     6. String() will be called on the underlying value if provided
//
// NIL values will lead to an empty string. Unsupported types are leading
// to their respective type name.
func (v *Value) String() string {
	if v.IsNil() {
		return ""
	}

	switch v.getResolvedValue().Kind() {
	case reflect.String:
		return v.getResolvedValue().String()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.getResolvedValue().Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(v.getResolvedValue().Uint(), 10)
	case reflect.Float32, reflect.Float64:
		return fmt.Sprintf("%f", v.getResolvedValue().Float())
	case reflect.Bool:
		if v.Bool() {
			return "True"
		} else {
			return "False"
		}
	case reflect.Struct:
		if t, ok := v.Interface().(fmt.Stringer); ok {
			return t.String()
		}
	}

	logf("Value.String() not implemented for type: %s\n", v.getResolvedValue().Kind().String())
	return v.getResolvedValue().String()
}

// Returns the underlying value as an integer (converts the underlying
// value, if necessary). If it's not possible to convert the underlying value,
// it will return 0.
func (v *Value) Integer() int {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return int(v.getResolvedValue().Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return int(v.getResolvedValue().Uint())
	case reflect.Float32, reflect.Float64:
		return int(v.getResolvedValue().Float())
	case reflect.String:
		// Try to convert from string to int (base 10)
		f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
		if err != nil {
			return 0
		}
		return int(f)
	default:
		logf("Value.Integer() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0
	}
}

// Returns the underlying value as a float (converts the underlying
// value, if necessary). If it's not possible to convert the underlying value,
// it will return 0.0.
func (v *Value) Float() float64 {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return float64(v.getResolvedValue().Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return float64(v.getResolvedValue().Uint())
	case reflect.Float32, reflect.Float64:
		return v.getResolvedValue().Float()
	case reflect.String:
		// Try to convert from string to float64 (base 10)
		f, err := strconv.ParseFloat(v.getResolvedValue().String(), 64)
		if err != nil {
			return 0.0
		}
		return f
	default:
		logf("Value.Float() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0.0
	}
}

// Returns the underlying value as bool. If the value is not bool, false
// will always be returned. If you're looking for true/false-evaluation of the
// underlying value, have a look on the IsTrue()-function.
func (v *Value) Bool() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Bool:
		return v.getResolvedValue().Bool()
	default:
		logf("Value.Bool() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Tries to evaluate the underlying value the Pythonic-way:
//
// Returns TRUE in one the following cases:
//
//     * int != 0
//     * uint != 0
//     * float != 0.0
//     * len(array/chan/map/slice/string) > 0
//     * bool == true
//     * underlying value is a struct
//
// Otherwise returns always FALSE.
func (v *Value) IsTrue() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.getResolvedValue().Int() != 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return v.getResolvedValue().Uint() != 0
	case reflect.Float32, reflect.Float64:
		return v.getResolvedValue().Float() != 0
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.getResolvedValue().Len() > 0
	case reflect.Bool:
		return v.getResolvedValue().Bool()
	case reflect.Struct:
		return true // struct instance is always true
	default:
		logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Tries to negate the underlying value. It's mainly used for
// the NOT-operator and in conjunction with a call to
// return_value.IsTrue() afterwards.
//
// Example:
//     AsValue(1).Negate().IsTrue() == false
func (v *Value) Negate() *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		if v.Integer() != 0 {
			return AsValue(0)
		} else {
			return AsValue(1)
		}
	case reflect.Float32, reflect.Float64:
		if v.Float() != 0.0 {
			return AsValue(float64(0.0))
		} else {
			return AsValue(float64(1.1))
		}
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return AsValue(v.getResolvedValue().Len() == 0)
	case reflect.Bool:
		return AsValue(!v.getResolvedValue().Bool())
	default:
		logf("Value.IsTrue() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue(true)
	}
}

// Returns the length for an array, chan, map, slice or string.
// Otherwise it will return 0.
func (v *Value) Len() int {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
		return v.getResolvedValue().Len()
	default:
		logf("Value.Len() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return 0
	}
}

// Slices an array, slice or string. Otherwise it will
// return an empty []int.
func (v *Value) Slice(i, j int) *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice, reflect.String:
		return AsValue(v.getResolvedValue().Slice(i, j).Interface())
	default:
		logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue([]int{})
	}
}

// Get the i-th item of an array, slice or string. Otherwise
// it will return NIL.
func (v *Value) Index(i int) *Value {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice:
		if i >= v.Len() {
			return AsValue(nil)
		}
		return AsValue(v.getResolvedValue().Index(i).Interface())
	case reflect.String:
		return AsValue(v.getResolvedValue().Slice(i, i+1).Interface())
	default:
		logf("Value.Slice() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return AsValue([]int{})
	}
}

// Checks whether the underlying value (which must be of type struct, map,
// string, array or slice) contains of another Value (e. g. used to check
// whether a struct contains of a specific field or a map contains a specific key).
//
// Example:
//     AsValue("Hello, World!").Contains(AsValue("World")) == true
func (v *Value) Contains(other *Value) bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Struct:
		field_value := v.getResolvedValue().FieldByName(other.String())
		return field_value.IsValid()
	case reflect.Map:
		var map_value reflect.Value
		switch other.Interface().(type) {
		case int:
			map_value = v.getResolvedValue().MapIndex(other.getResolvedValue())
		case string:
			map_value = v.getResolvedValue().MapIndex(other.getResolvedValue())
		default:
			logf("Value.Contains() does not support lookup type '%s'\n", other.getResolvedValue().Kind().String())
			return false
		}

		return map_value.IsValid()
	case reflect.String:
		return strings.Contains(v.getResolvedValue().String(), other.String())

	// TODO: reflect.Array, reflect.Slice

	default:
		logf("Value.Contains() not available for type: %s\n", v.getResolvedValue().Kind().String())
		return false
	}
}

// Checks whether the underlying value is of type array, slice or string.
// You normally would use CanSlice() before using the Slice() operation.
func (v *Value) CanSlice() bool {
	switch v.getResolvedValue().Kind() {
	case reflect.Array, reflect.Slice, reflect.String:
		return true
	}
	return false
}

// Iterates over a map, array, slice or a string. It calls the
// function's first argument for every value with the following arguments:
//
//     idx      current 0-index
//     count    total amount of items
//     key      *Value for the key or item
//     value    *Value (only for maps, the respective value for a specific key)
//
// If the underlying value has no items or is not one of the types above,
// the empty function (function's second argument) will be called.
func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func()) {
	switch v.getResolvedValue().Kind() {
	case reflect.Map:
		keys := v.getResolvedValue().MapKeys()
		keyLen := len(keys)
		for idx, key := range keys {
			value := v.getResolvedValue().MapIndex(key)
			if !fn(idx, keyLen, &Value{key}, &Value{value}) {
				return
			}
		}
		if keyLen == 0 {
			empty()
		}
		return // done
	case reflect.Array, reflect.Slice:
		itemCount := v.getResolvedValue().Len()
		if itemCount > 0 {
			for i := 0; i < itemCount; i++ {
				if !fn(i, itemCount, &Value{v.getResolvedValue().Index(i)}, nil) {
					return
				}
			}
		} else {
			empty()
		}
		return // done
	case reflect.String:
		// TODO: Not utf8-compatible (utf8-decoding neccessary)
		charCount := v.getResolvedValue().Len()
		if charCount > 0 {
			for i := 0; i < charCount; i++ {
				if !fn(i, charCount, &Value{v.getResolvedValue().Slice(i, i+1)}, nil) {
					return
				}
			}
		} else {
			empty()
		}
		return // done
	default:
		logf("Value.Iterate() not available for type: %s\n", v.getResolvedValue().Kind().String())
	}
	empty()
}

// Gives you access to the underlying value.
func (v *Value) Interface() interface{} {
	if v.v.IsValid() {
		return v.v.Interface()
	}
	return nil
}

// Checks whether two values are containing the same value or object.
func (v *Value) EqualValueTo(other *Value) bool {
	return v.Interface() == other.Interface()
}
<|endoftext|>