<commit_before>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"math"
)

// txValidate is used to track results of validating scripts for each
// transaction input index.
type txValidate struct {
	txIndex int
	err     error
}

// validateTxIn validates a the script pair for the passed spending transaction
// (along with the specific input index) and origin transaction (with the
// specific output index).
func validateTxIn(txInIdx int, txin *btcwire.TxIn, tx *btcutil.Tx, originTx *btcutil.Tx, flags btcscript.ScriptFlags) error {
	// If the input transaction has no previous input, there is nothing
	// to check.
	originTxIdx := txin.PreviousOutpoint.Index
	if originTxIdx == math.MaxUint32 {
		return nil
	}

	if originTxIdx >= uint32(len(originTx.MsgTx().TxOut)) {
		originTxSha := &txin.PreviousOutpoint.Hash
		log.Warnf("unable to locate source tx %v spending tx %v",
			originTxSha, tx.Sha())
		return fmt.Errorf("invalid index %x", originTxIdx)
	}

	sigScript := txin.SignatureScript
	pkScript := originTx.MsgTx().TxOut[originTxIdx].PkScript
	engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx,
		tx.MsgTx(), flags)
	if err != nil {
		return err
	}

	err = engine.Execute()
	if err != nil {
		log.Warnf("validate of input %v failed: %v", txInIdx, err)
		return err
	}

	return nil
}

// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
func ValidateTransactionScripts(tx *btcutil.Tx, txStore TxStore, flags btcscript.ScriptFlags) (err error) {
	c := make(chan txValidate)
	job := tx.MsgTx().TxIn
	resultErrors := make([]error, len(job))

	var currentItem int
	var completedItems int

	processFunc := func(txInIdx int) {
		log.Tracef("validating tx %v input %v len %v",
			tx.Sha(), currentItem, len(job))
		txin := job[txInIdx]
		originTxSha := &txin.PreviousOutpoint.Hash
		origintxidx := txin.PreviousOutpoint.Index

		var originTx *btcutil.Tx
		if origintxidx != math.MaxUint32 {
			txInfo, ok := txStore[*originTxSha]
			if !ok {
				//wtf?
				fmt.Printf("obj not found in txStore %v",
					originTxSha)
			}
			originTx = txInfo.Tx
		}
		err := validateTxIn(txInIdx, txin, tx, originTx, flags)
		r := txValidate{txInIdx, err}
		c <- r
	}
	for currentItem = 0; currentItem < len(job) && currentItem < 16; currentItem++ {
		go processFunc(currentItem)
	}
	for completedItems < len(job) {
		select {
		case result := <-c:
			completedItems++
			resultErrors[result.txIndex] = result.err
			// would be nice to determine if we could stop
			// on early errors here instead of running more.
			if err == nil {
				err = result.err
			}

			if currentItem < len(job) {
				go processFunc(currentItem)
				currentItem++
			}
		}
	}
	for i := 0; i < len(job); i++ {
		if resultErrors[i] != nil {
			log.Warnf("tx %v failed input %v, err %v", tx.Sha(), i,
				resultErrors[i])
		}
	}
	return
}

// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block.
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
	// Setup the script validation flags.  Blocks created after the BIP0016
	// activation time need to have the pay-to-script-hash checks enabled.
	var flags btcscript.ScriptFlags
	if block.MsgBlock().Header.Timestamp.After(btcscript.Bip16Activation) {
		flags |= btcscript.ScriptBip16
	}

	txList := block.Transactions()
	c := make(chan txValidate)
	resultErrors := make([]error, len(txList))

	var currentItem int
	var completedItems int
	processFunc := func(txIdx int) {
		err := ValidateTransactionScripts(txList[txIdx], txStore, flags)
		r := txValidate{txIdx, err}
		c <- r
	}
	for currentItem = 0; currentItem < len(txList) && currentItem < 8; currentItem++ {
		go processFunc(currentItem)
	}
	for completedItems < len(txList) {
		select {
		case result := <-c:
			completedItems++
			resultErrors[result.txIndex] = result.err
			// would be nice to determine if we could stop
			// on early errors here instead of running more.

			if currentItem < len(txList) {
				go processFunc(currentItem)
				currentItem++
			}
		}
	}
	for i := 0; i < len(txList); i++ {
		if resultErrors[i] != nil {
			return resultErrors[i]
		}
	}

	return nil
}
<commit_msg>use passed in parameter and not global in log message.<commit_after>// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"math"
)

// txValidate is used to track results of validating scripts for each
// transaction input index.
type txValidate struct {
	txIndex int
	err     error
}

// validateTxIn validates a the script pair for the passed spending transaction
// (along with the specific input index) and origin transaction (with the
// specific output index).
func validateTxIn(txInIdx int, txin *btcwire.TxIn, tx *btcutil.Tx, originTx *btcutil.Tx, flags btcscript.ScriptFlags) error {
	// If the input transaction has no previous input, there is nothing
	// to check.
	originTxIdx := txin.PreviousOutpoint.Index
	if originTxIdx == math.MaxUint32 {
		return nil
	}

	if originTxIdx >= uint32(len(originTx.MsgTx().TxOut)) {
		originTxSha := &txin.PreviousOutpoint.Hash
		log.Warnf("unable to locate source tx %v spending tx %v",
			originTxSha, tx.Sha())
		return fmt.Errorf("invalid index %x", originTxIdx)
	}

	sigScript := txin.SignatureScript
	pkScript := originTx.MsgTx().TxOut[originTxIdx].PkScript
	engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx,
		tx.MsgTx(), flags)
	if err != nil {
		return err
	}

	err = engine.Execute()
	if err != nil {
		log.Warnf("validate of input %v failed: %v", txInIdx, err)
		return err
	}

	return nil
}

// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
func ValidateTransactionScripts(tx *btcutil.Tx, txStore TxStore, flags btcscript.ScriptFlags) (err error) {
	c := make(chan txValidate)
	job := tx.MsgTx().TxIn
	resultErrors := make([]error, len(job))

	var currentItem int
	var completedItems int

	processFunc := func(txInIdx int) {
		log.Tracef("validating tx %v input %v len %v",
			tx.Sha(), txInIdx, len(job))
		txin := job[txInIdx]
		originTxSha := &txin.PreviousOutpoint.Hash
		origintxidx := txin.PreviousOutpoint.Index

		var originTx *btcutil.Tx
		if origintxidx != math.MaxUint32 {
			txInfo, ok := txStore[*originTxSha]
			if !ok {
				//wtf?
				fmt.Printf("obj not found in txStore %v",
					originTxSha)
			}
			originTx = txInfo.Tx
		}
		err := validateTxIn(txInIdx, txin, tx, originTx, flags)
		r := txValidate{txInIdx, err}
		c <- r
	}
	for currentItem = 0; currentItem < len(job) && currentItem < 16; currentItem++ {
		go processFunc(currentItem)
	}
	for completedItems < len(job) {
		select {
		case result := <-c:
			completedItems++
			resultErrors[result.txIndex] = result.err
			// would be nice to determine if we could stop
			// on early errors here instead of running more.
			if err == nil {
				err = result.err
			}

			if currentItem < len(job) {
				go processFunc(currentItem)
				currentItem++
			}
		}
	}
	for i := 0; i < len(job); i++ {
		if resultErrors[i] != nil {
			log.Warnf("tx %v failed input %v, err %v", tx.Sha(), i,
				resultErrors[i])
		}
	}
	return
}

// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block.
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
	// Setup the script validation flags.  Blocks created after the BIP0016
	// activation time need to have the pay-to-script-hash checks enabled.
	var flags btcscript.ScriptFlags
	if block.MsgBlock().Header.Timestamp.After(btcscript.Bip16Activation) {
		flags |= btcscript.ScriptBip16
	}

	txList := block.Transactions()
	c := make(chan txValidate)
	resultErrors := make([]error, len(txList))

	var currentItem int
	var completedItems int
	processFunc := func(txIdx int) {
		err := ValidateTransactionScripts(txList[txIdx], txStore, flags)
		r := txValidate{txIdx, err}
		c <- r
	}
	for currentItem = 0; currentItem < len(txList) && currentItem < 8; currentItem++ {
		go processFunc(currentItem)
	}
	for completedItems < len(txList) {
		select {
		case result := <-c:
			completedItems++
			resultErrors[result.txIndex] = result.err
			// would be nice to determine if we could stop
			// on early errors here instead of running more.

			if currentItem < len(txList) {
				go processFunc(currentItem)
				currentItem++
			}
		}
	}
	for i := 0; i < len(txList); i++ {
		if resultErrors[i] != nil {
			return resultErrors[i]
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package mdb

import (
	"fmt"
	"github.com/mabetle/mcore"
)

// PrintTable prints table
func (s *Sql) PrintTable(table string) {
	s.PrintTableFriendly(table)
}

// PrintTableVertical prints table vertical
func (s *Sql) PrintTableVertical(table string) {
	s.PrintQueryVertical("select * from " + table)
}

// PrintTableInJSONFormat prints table in JSON format.
func (s *Sql) PrintTableInJSONFormat(table string) {
	datas, columns, err := s.GetTableRowsMap(table)
	fmt.Println("\n===Begin Print Table: ", table, "====")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		PrintMapWithColumns(datas, columns)
	}
	fmt.Println("\n===End.. Print Table: ", table, "====")
}

//PrintTableFriendly print table friendly.
func (s *Sql) PrintTableFriendly(table string) {
	s.PrintQueryFriendly("select * from " + table)
}

//PrintQuery print query.
func (s *Sql) PrintQuery(q string, args ...interface{}) {
	s.PrintQueryFriendly(q, args...)
}

// PrintTableQuery print table query.
func (s *Sql) PrintTableQuery(table string, ql string, args ...interface{}) {
	sql := "select * from " + table + " "
	sql = BuildWhereQuery(sql, ql)
	s.PrintQuery(sql, args...)
}

// PrintTableQueryVertical print table query vertical.
func (s *Sql) PrintTableQueryVertical(table string, ql string, args ...interface{}) {
	q := "select * from " + table + " "
	q = BuildWhereQuery(q, ql)
	s.PrintQueryVertical(q, args...)
}

// PrintQueryInJSONFormat prints query in JSON format.
func (s *Sql) PrintQueryInJSONFormat(sql string, args ...interface{}) {
	fmt.Println("\n============ Begin Print Query Json =================")
	datas, columns, err := s.QueryForMaps(sql, args...)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		PrintMapWithColumns(datas, columns)
	}
	fmt.Println("============ End  Print Query Json =================")
}

func maxIndexLen(data [][]string, index int) int {
	max := 10
	for _, row := range data {
		cols := len(row)
		// index out of range
		if cols < index {
			break
		}
		rn := mcore.StringWidth(row[index])
		if rn > max {
			max = rn
		}
	}
	return max + 2
}

func maxLen(data [][]string) []int {
	if len(data) < 1 {
		return []int{}
	}
	// first line
	n := len(data[0])
	maxLen := make([]int, n)
	for index := 0; index < n; index++ {
		maxLen[index] = maxIndexLen(data, index)
	}
	return maxLen
}

func maxCol(row []string) int {
	n := 10
	for _, v := range row {
		vn := mcore.StringWidth(v)
		if vn > n {
			n = vn
		}
	}
	return n + 2
}

// PrintArrayFriendly
func PrintArrayFriendly(data [][]string) {
	// no data
	if len(data) == 0 {
		return
	}
	maxLen := maxLen(data)
	//fmt.Printf("%v\n", maxLen)
	for _, row := range data {
		for i, v := range row {
			if i < len(maxLen) {
				v = mcore.GetFixedWidthStringAlignLeft(v, maxLen[i])
			}
			fmt.Printf("%s", v)
		}
		fmt.Printf("\n")
	}
}

func PrintArrayVerticalFriendly(data [][]string) {
	if len(data) == 0 {
		fmt.Printf("no results.\n")
		return
	}
	hd := data[0]
	maxCol := maxCol(hd)
	for k, row := range data {
		if k == 0 {
			// skip head line
			continue
		}
		for i, v := range row {
			colLabel := ""
			if i < len(hd) {
				colLabel = mcore.GetFixedWidthStringAlignRight(hd[i], maxCol)
			}
			fmt.Printf("%s:%s\n", colLabel, v)
		}
		fmt.Printf("\n")
	}
}

// PrintQueryFriendly prints query friendly.
func (s *Sql) PrintQueryFriendly(q string, args ...interface{}) {
	data := s.QueryForArrayWithHeader(q, args...)
	fmt.Println("\n============ Begin Print Query Friendly =================")
	PrintArrayFriendly(data)
	fmt.Println("============ End.. Print Query Friendly =================")
}

// PrintQueryVertical prints query vertical.
func (s *Sql) PrintQueryVertical(q string, args ...interface{}) {
	data := s.QueryForArrayWithHeader(q, args...)
	fmt.Println("\n============ Begin Print Query Vertical =================")
	PrintArrayVerticalFriendly(data)
	fmt.Println("\n============ End.. Print Query Vertical =================")
}
<commit_msg>fix print<commit_after>package mdb

import (
	"fmt"
	"github.com/mabetle/mcore"
)

// PrintTable prints table
func (s *Sql) PrintTable(table string) {
	s.PrintTableFriendly(table)
}

// PrintTableVertical prints table vertical
func (s *Sql) PrintTableVertical(table string) {
	s.PrintQueryVertical("select * from " + table)
}

// PrintTableInJSONFormat prints table in JSON format.
func (s *Sql) PrintTableInJSONFormat(table string) {
	datas, columns, err := s.GetTableRowsMap(table)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		PrintMapWithColumns(datas, columns)
	}
}

//PrintTableFriendly print table friendly.
func (s *Sql) PrintTableFriendly(table string) {
	s.PrintQueryFriendly("select * from " + table)
}

//PrintQuery print query.
func (s *Sql) PrintQuery(q string, args ...interface{}) {
	s.PrintQueryFriendly(q, args...)
}

// PrintTableQuery print table query.
func (s *Sql) PrintTableQuery(table string, ql string, args ...interface{}) {
	sql := "select * from " + table + " "
	sql = BuildWhereQuery(sql, ql)
	s.PrintQuery(sql, args...)
}

// PrintTableQueryVertical print table query vertical.
func (s *Sql) PrintTableQueryVertical(table string, ql string, args ...interface{}) {
	q := "select * from " + table + " "
	q = BuildWhereQuery(q, ql)
	s.PrintQueryVertical(q, args...)
}

// PrintQueryInJSONFormat prints query in JSON format.
func (s *Sql) PrintQueryInJSONFormat(sql string, args ...interface{}) {
	datas, columns, err := s.QueryForMaps(sql, args...)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		PrintMapWithColumns(datas, columns)
	}
}

func maxIndexLen(data [][]string, index int) int {
	max := 10
	for _, row := range data {
		cols := len(row)
		// index out of range
		if cols < index {
			break
		}
		rn := mcore.StringWidth(row[index])
		if rn > max {
			max = rn
		}
	}
	return max + 2
}

func maxLen(data [][]string) []int {
	if len(data) < 1 {
		return []int{}
	}
	// first line
	n := len(data[0])
	maxLen := make([]int, n)
	for index := 0; index < n; index++ {
		maxLen[index] = maxIndexLen(data, index)
	}
	return maxLen
}

func maxCol(row []string) int {
	n := 10
	for _, v := range row {
		vn := mcore.StringWidth(v)
		if vn > n {
			n = vn
		}
	}
	return n + 2
}

// PrintArrayFriendly
func PrintArrayFriendly(data [][]string) {
	// no data
	if len(data) == 0 {
		return
	}
	maxLen := maxLen(data)
	//fmt.Printf("%v\n", maxLen)
	for _, row := range data {
		for i, v := range row {
			if i < len(maxLen) {
				v = mcore.GetFixedWidthStringAlignLeft(v, maxLen[i])
			}
			fmt.Printf("%s", v)
		}
		fmt.Printf("\n")
	}
}

func PrintArrayVerticalFriendly(data [][]string) {
	if len(data) == 0 {
		fmt.Printf("no results.\n")
		return
	}
	hd := data[0]
	maxCol := maxCol(hd)
	for k, row := range data {
		if k == 0 {
			// skip head line
			continue
		}
		for i, v := range row {
			colLabel := ""
			if i < len(hd) {
				colLabel = mcore.GetFixedWidthStringAlignRight(hd[i], maxCol)
			}
			fmt.Printf("%s:%s\n", colLabel, v)
		}
		fmt.Printf("\n")
	}
}

// PrintQueryFriendly prints query friendly.
func (s *Sql) PrintQueryFriendly(q string, args ...interface{}) {
	data := s.QueryForArrayWithHeader(q, args...)
	PrintArrayFriendly(data)
}

// PrintQueryVertical prints query vertical.
func (s *Sql) PrintQueryVertical(q string, args ...interface{}) {
	data := s.QueryForArrayWithHeader(q, args...)
	PrintArrayVerticalFriendly(data)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/db/keyval/kvproto"
	"github.com/ligato/cn-infra/examples/model"
	"github.com/ligato/cn-infra/flavors/etcdkafka"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logroot"
	"github.com/ligato/cn-infra/servicelabel"
	"github.com/ligato/cn-infra/utils/config"
)

// *************************************************************************
// This file contains examples of simple Data Broker CRUD operations
// (APIs) including an event handler (watcher). The CRUD operations
// supported by the Data Broker are as follows:
// - Create/Update: dataBroker.Put()
// - Read:          dataBroker.Get()
// - Delete:        dataBroker.Delete()
//
// These functions are called from the REST API. CRUD operations are
// done as single operations and as a part of the transaction
// ************************************************************************/

/********
 * Main *
 ********/

var log logging.Logger

// Main allows running Example Plugin as a statically linked binary with Agent Core Plugins. Close channel and plugins
// required for the example are initialized. Agent is instantiated with generic plugins (ETCD, Kafka, Status check,
// HTTP and Log), resync plugin and example plugin which demonstrates ETCD functionality.
func main() {
	log = logroot.StandardLogger()
	// Init close channel to stop the example
	closeChannel := make(chan struct{}, 1)

	flavor := etcdkafka.Flavor{}
	// Resync plugin
	resyncPlugin := &core.NamedPlugin{PluginName: resync.PluginID, Plugin: &resync.Plugin{}}
	// Example plugin (ETCD)
	examplePlugin := &core.NamedPlugin{PluginName: PluginID, Plugin: &ExamplePlugin{ServiceLabel: &flavor.ServiceLabel}}

	// Create new agent
	agent := core.NewAgent(log, 15*time.Second, append(flavor.Plugins(), resyncPlugin, examplePlugin)...)

	// End when the ETCD example is finished
	go closeExample("etcd txn example finished", closeChannel)

	core.EventLoopWithInterrupt(agent, closeChannel)
}

// Stop the agent with desired info message
func closeExample(message string, closeChannel chan struct{}) {
	time.Sleep(12 * time.Second)
	log.Info(message)
	closeChannel <- struct{}{}
}

/**********************
 * Example plugin API *
 **********************/

// PluginID of the custom ETCD plugin
const PluginID core.PluginName = "example-plugin"

/******************
 * Example plugin *
 ******************/

// ExamplePlugin implements Plugin interface which is used to pass custom plugin instances to the agent
type ExamplePlugin struct {
	ServiceLabel        servicelabel.ReaderAPI
	exampleConfigurator *ExampleConfigurator       // Plugin configurator
	transport           datasync.TransportAdapter  // To access ETCD data
	changeChannel       chan datasync.ChangeEvent  // Channel used by the watcher for change events
	resyncChannel       chan datasync.ResyncEvent  // Channel used by the watcher for resync events
	watchDataReg        datasync.WatchRegistration // To subscribe on data change/resync events
}

// Init is the entry point into the plugin that is called by Agent Core when the Agent is coming up.
// The Go native plugin mechanism that was introduced in Go 1.8
func (plugin *ExamplePlugin) Init() error {
	// Initialize plugin fields
	plugin.exampleConfigurator = &ExampleConfigurator{plugin.ServiceLabel}
	plugin.resyncChannel = make(chan datasync.ResyncEvent)
	plugin.changeChannel = make(chan datasync.ChangeEvent)

	// Start the consumer (ETCD watcher) before the custom plugin configurator is initialized
	go plugin.consumer()

	// Now initialize the plugin configurator
	plugin.exampleConfigurator.Init()

	// Subscribe watcher to be able to watch on data changes and resync events
	plugin.subscribeWatcher()

	log.Info("Initialization of the custom plugin for the ETCD example is completed")

	return nil
}

// Close is called by Agent Core when the Agent is shutting down. It is supposed to clean up resources that were
// allocated by the plugin during its lifetime
func (plugin *ExamplePlugin) Close() error {
	plugin.exampleConfigurator.Close()
	plugin.watchDataReg.Close()
	return nil
}

/*************************
 * Example plugin config *
 *************************/

// ExampleConfigurator usually initializes configuration-specific fields or other tasks (e.g. defines GOVPP channels
// if they are used, checks VPP message compatibility etc.)
type ExampleConfigurator struct {
	ServiceLabel servicelabel.ReaderAPI
}

// Init members of configurator
func (configurator *ExampleConfigurator) Init() (err error) {
	// There is nothing to init in the example
	log.Info("Custom plugin configurator initialized")

	// Now the configurator is initialized and the watcher is already running (started in plugin initialization),
	// so publisher is used to put data to ETCD
	go func() {
		// Show simple ETCD CRUD
		configurator.etcdPublisher()
		// Show transactions
		configurator.etcdTxnPublisher()
	}()

	return err
}

// Close function for example plugin (just for representation, there is nothing to close in the example)
func (configurator *ExampleConfigurator) Close() {}

/*************
 * ETCD call *
 *************/

const etcdIndex string = "index"

// KeyProtoValWriter creates a simple data, then demonstrates CRUD operations with ETCD
func (configurator *ExampleConfigurator) etcdPublisher() {
	// Get data broker to communicate with ETCD
	cfg := &etcdv3.Config{}

	configFile := os.Getenv("ETCDV3_CONFIG")
	if configFile != "" {
		err := config.ParseConfigFromYamlFile(configFile, cfg)
		if err != nil {
			log.Fatal(err)
		}
	}
	etcdConfig, err := etcdv3.ConfigToClientv3(cfg)
	if err != nil {
		log.Fatal(err)
	}

	bDB, _ := etcdv3.NewEtcdConnectionWithBytes(*etcdConfig, log)
	dataBroker := kvproto.NewProtoWrapperWithSerializer(bDB, &keyval.SerializerJSON{}).
		NewBroker(configurator.ServiceLabel.GetAgentPrefix())

	time.Sleep(3 * time.Second)

	// Convert data to the generated proto format
	exampleData := configurator.buildData("string1", 0, true)

	// PUT: examplePut demonstrates how to use the Data Broker Put() API to create (or update) a simple data
	// structure into ETCD
	dataBroker.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), exampleData)

	// Prepare different set of data
	exampleData = configurator.buildData("string2", 1, false)

	// UPDATE: Put() performs both create operations (if index does not exist) and update operations
	// (if the index exists)
	dataBroker.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), exampleData)

	// GET: exampleGet demonstrates how to use the Data Broker Get() API to read a simple data structure from ETCD
	result := etcd_example.EtcdExample{}
	found, _, err := dataBroker.GetValue(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), &result)
	if err != nil {
		log.Error(err)
	}
	if found {
		log.Infof("Data read from ETCD data store. Values: %v, %v, %v",
			result.StringVal, result.Uint32Val, result.BoolVal)
	} else {
		log.Error("Data not found")
	}

	// DELETE: demonstrates how to use the Data Broker Delete() API to delete a simple data structure from ETCD
	dataBroker.Delete(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex))
}

// KeyProtoValWriter creates a simple data, then demonstrates transaction operations with ETCD
func (configurator *ExampleConfigurator) etcdTxnPublisher() {
	log.Info("Preparing bridge domain data")
	// Get data broker to communicate with ETCD
	cfg := &etcdv3.Config{}

	configFile := os.Getenv("ETCDV3_CONFIG")
	if configFile != "" {
		err := config.ParseConfigFromYamlFile(configFile, cfg)
		if err != nil {
			log.Fatal(err)
		}
	}
	etcdConfig, err := etcdv3.ConfigToClientv3(cfg)
	if err != nil {
		log.Fatal(err)
	}

	bDB, _ := etcdv3.NewEtcdConnectionWithBytes(*etcdConfig, log)
	dataBroker := kvproto.NewProtoWrapperWithSerializer(bDB, &keyval.SerializerJSON{}).
		NewBroker(configurator.ServiceLabel.GetAgentPrefix())

	time.Sleep(3 * time.Second)

	// This is how to use the Data Broker Txn API to create a new transaction. It is called from the HTTP handler
	// when a user triggers the creation of a new transaction via REST
	putTxn := dataBroker.NewTxn()
	for i := 1; i <= 3; i++ {
		exampleData1 := configurator.buildData("string", uint32(i), true)
		// putTxn.Put demonstrates how to use the Data Broker Txn Put() API. It is called from the HTTP handler
		// when a user invokes the REST API to add a new Put() operation to the transaction
		putTxn = putTxn.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex+strconv.Itoa(i)), exampleData1)
	}
	// putTxn.Commit() demonstrates how to use the Data Broker Txn Commit() API. It is called from the HTTP handler
	// when a user invokes the REST API to commit a transaction.
	err = putTxn.Commit()
	if err != nil {
		log.Error(err)
	}
	// Another transaction chain to demonstrate delete operations. Put and Delete operations can be used together
	// within one transaction
	deleteTxn := dataBroker.NewTxn()
	for i := 1; i <= 3; i++ {
		// deleteTxn.Delete demonstrates how to use the Data Broker Txn Delete() API. It is called from the
		// HTTP handler when a user invokes the REST API to add a new Delete() operation to the transaction.
		// Put and Delete operations can be combined in the same transaction chain
		deleteTxn = deleteTxn.Delete(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex+strconv.Itoa(i)))
	}

	// Commit transactions to data store. Transaction executes multiple operations in a more efficient way in
	// contrast to executing them one by one.
	err = deleteTxn.Commit()
	if err != nil {
		log.Error(err)
	}
}

// The ETCD key prefix used for this example
func etcdKeyPrefix(agentLabel string) string {
	return "/vnf-agent/" + agentLabel + "/api/v1/example/db/simple/"
}

// The ETCD key (the key prefix + label)
func etcdKeyPrefixLabel(agentLabel string, index string) string {
	return etcdKeyPrefix(agentLabel) + index
}

/***********
 * KeyValProtoWatcher *
 ***********/

// Consumer (watcher) is subscribed to watch on data store changes. Change arrives via data change channel and
// its key is parsed
func (plugin *ExamplePlugin) consumer() {
	log.Print("KeyValProtoWatcher started")
	for {
		select {
		case dataChng := <-plugin.changeChannel:
			// If event arrives, the key is extracted and used together with the expected prefix to
			// identify item
			key := dataChng.GetKey()
			if strings.HasPrefix(key, etcdKeyPrefix(plugin.ServiceLabel.GetAgentLabel())) {
				var value, previousValue etcd_example.EtcdExample
				// The first return value is diff - boolean flag whether previous value exists or not
				err := dataChng.GetValue(&value)
				if err != nil {
					log.Error(err)
				}
				diff, err := dataChng.GetPrevValue(&previousValue)
				if err != nil {
					log.Error(err)
				}
				log.Infof("Event arrived to etcd eventHandler, key %v, update: %v, change type: %v,",
					dataChng.GetKey(), diff, dataChng.GetChangeType())
			}
			// Another strings.HasPrefix(key, etcd prefix) ...
		}
	}
}

// KeyValProtoWatcher is subscribed to data change channel and resync channel. ETCD transport adapter is used for this purpose
func (plugin *ExamplePlugin) subscribeWatcher() (err error) {
	plugin.watchDataReg, err = plugin.transport.
		Watch("Example etcd plugin", plugin.changeChannel, plugin.resyncChannel, etcdKeyPrefix(plugin.ServiceLabel.GetAgentLabel()))
	if err != nil {
		return err
	}

	log.Info("KeyValProtoWatcher subscribed")

	return nil
}

// Create simple ETCD data structure with provided data values
func (configurator *ExampleConfigurator) buildData(stringVal string, uint32Val uint32, boolVal bool) *etcd_example.EtcdExample {
	return &etcd_example.EtcdExample{
		StringVal: stringVal,
		Uint32Val: uint32Val,
		BoolVal:   boolVal,
	}
}
<commit_msg>ODPM-361 fix resync move events go files<commit_after>package main

import (
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/db/keyval/kvproto"
	"github.com/ligato/cn-infra/examples/model"
	"github.com/ligato/cn-infra/flavors/etcdkafka"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logroot"
	"github.com/ligato/cn-infra/servicelabel"
	"github.com/ligato/cn-infra/utils/config"
)

// *************************************************************************
// This file contains examples of simple Data Broker CRUD operations
// (APIs) including an event handler (watcher). The CRUD operations
// supported by the Data Broker are as follows:
// - Create/Update: dataBroker.Put()
// - Read:          dataBroker.Get()
// - Delete:        dataBroker.Delete()
//
// These functions are called from the REST API. CRUD operations are
// done as single operations and as a part of the transaction
// ************************************************************************/

/********
 * Main *
 ********/

var log logging.Logger

// Main allows running Example Plugin as a statically linked binary with Agent Core Plugins. Close channel and plugins
// required for the example are initialized. Agent is instantiated with generic plugins (ETCD, Kafka, Status check,
// HTTP and Log), resync plugin and example plugin which demonstrates ETCD functionality.
func main() {
	log = logroot.StandardLogger()
	// Init close channel to stop the example
	closeChannel := make(chan struct{}, 1)

	flavor := etcdkafka.Flavor{}
	// Resync plugin
	resyncPlugin := &core.NamedPlugin{PluginName: resync.PluginID, Plugin: &resync.Plugin{}}
	// Example plugin (ETCD)
	examplePlugin := &core.NamedPlugin{PluginName: PluginID, Plugin: &ExamplePlugin{ServiceLabel: &flavor.ServiceLabel}}

	// Create new agent
	agent := core.NewAgent(log, 15*time.Second, append(flavor.Plugins(), resyncPlugin, examplePlugin)...)

	// End when the ETCD example is finished
	go closeExample("etcd txn example finished", closeChannel)

	core.EventLoopWithInterrupt(agent, closeChannel)
}

// Stop the agent with desired info message
func closeExample(message string, closeChannel chan struct{}) {
	time.Sleep(12 * time.Second)
	log.Info(message)
	closeChannel <- struct{}{}
}

/**********************
 * Example plugin API *
 **********************/

// PluginID of the custom ETCD plugin
const PluginID core.PluginName = "example-plugin"

/******************
 * Example plugin *
 ******************/

// ExamplePlugin implements Plugin interface which is used to pass custom plugin instances to the agent
type ExamplePlugin struct {
	ServiceLabel        servicelabel.ReaderAPI
	exampleConfigurator *ExampleConfigurator       // Plugin configurator
	transport           datasync.KeyValProtoWatcher  // To access ETCD data
	changeChannel       chan datasync.ChangeEvent  // Channel used by the watcher for change events
	resyncChannel       chan datasync.ResyncEvent  // Channel used by the watcher for resync events
	watchDataReg        datasync.WatchRegistration // To subscribe on data change/resync events
}

// Init is the entry point into the plugin that is called by Agent Core when the Agent is coming up.
// The Go native plugin mechanism that was introduced in Go 1.8
func (plugin *ExamplePlugin) Init() error {
	// Initialize plugin fields
	plugin.exampleConfigurator = &ExampleConfigurator{plugin.ServiceLabel}
	plugin.resyncChannel = make(chan datasync.ResyncEvent)
	plugin.changeChannel = make(chan datasync.ChangeEvent)

	// Start the consumer (ETCD watcher) before the custom plugin configurator is initialized
	go plugin.consumer()

	// Now initialize the plugin configurator
	plugin.exampleConfigurator.Init()

	// Subscribe watcher to be able to watch on data changes and resync events
	plugin.subscribeWatcher()

	log.Info("Initialization of the custom plugin for the ETCD example is completed")

	return nil
}

// Close is called by Agent Core when the Agent is shutting down. It is supposed to clean up resources that were
// allocated by the plugin during its lifetime
func (plugin *ExamplePlugin) Close() error {
	plugin.exampleConfigurator.Close()
	plugin.watchDataReg.Close()
	return nil
}

/*************************
 * Example plugin config *
 *************************/

// ExampleConfigurator usually initializes configuration-specific fields or other tasks (e.g. defines GOVPP channels
// if they are used, checks VPP message compatibility etc.)
type ExampleConfigurator struct {
	ServiceLabel servicelabel.ReaderAPI
}

// Init members of configurator
func (configurator *ExampleConfigurator) Init() (err error) {
	// There is nothing to init in the example
	log.Info("Custom plugin configurator initialized")

	// Now the configurator is initialized and the watcher is already running (started in plugin initialization),
	// so publisher is used to put data to ETCD
	go func() {
		// Show simple ETCD CRUD
		configurator.etcdPublisher()
		// Show transactions
		configurator.etcdTxnPublisher()
	}()

	return err
}

// Close function for example plugin (just for representation, there is nothing to close in the example)
func (configurator *ExampleConfigurator) Close() {}

/*************
 * ETCD call *
 *************/

const etcdIndex string = "index"

// KeyProtoValWriter creates a simple data, then demonstrates CRUD operations with ETCD
func (configurator *ExampleConfigurator) etcdPublisher() {
	// Get data broker to communicate with ETCD
	cfg := &etcdv3.Config{}

	configFile := os.Getenv("ETCDV3_CONFIG")
	if configFile != "" {
		err := config.ParseConfigFromYamlFile(configFile, cfg)
		if err != nil {
			log.Fatal(err)
		}
	}
	etcdConfig, err := etcdv3.ConfigToClientv3(cfg)
	if err != nil {
		log.Fatal(err)
	}

	bDB, _ := etcdv3.NewEtcdConnectionWithBytes(*etcdConfig, log)
	dataBroker := kvproto.NewProtoWrapperWithSerializer(bDB, &keyval.SerializerJSON{}).
		NewBroker(configurator.ServiceLabel.GetAgentPrefix())

	time.Sleep(3 * time.Second)

	// Convert data to the generated proto format
	exampleData := configurator.buildData("string1", 0, true)

	// PUT: examplePut demonstrates how to use the Data Broker Put() API to create (or update) a simple data
	// structure into ETCD
	dataBroker.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), exampleData)

	// Prepare different set of data
	exampleData = configurator.buildData("string2", 1, false)

	// UPDATE: Put() performs both create operations (if index does not exist) and update operations
	// (if the index exists)
	dataBroker.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), exampleData)

	// GET: exampleGet demonstrates how to use the Data Broker Get() API to read a simple data structure from ETCD
	result := etcd_example.EtcdExample{}
	found, _, err := dataBroker.GetValue(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex), &result)
	if err != nil {
		log.Error(err)
	}
	if found {
		log.Infof("Data read from ETCD data store. Values: %v, %v, %v",
			result.StringVal, result.Uint32Val, result.BoolVal)
	} else {
		log.Error("Data not found")
	}

	// DELETE: demonstrates how to use the Data Broker Delete() API to delete a simple data structure from ETCD
	dataBroker.Delete(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex))
}

// KeyProtoValWriter creates a simple data, then demonstrates transaction operations with ETCD
func (configurator *ExampleConfigurator) etcdTxnPublisher() {
	log.Info("Preparing bridge domain data")
	// Get data broker to communicate with ETCD
	cfg := &etcdv3.Config{}

	configFile := os.Getenv("ETCDV3_CONFIG")
	if configFile != "" {
		err := config.ParseConfigFromYamlFile(configFile, cfg)
		if err != nil {
			log.Fatal(err)
		}
	}
	etcdConfig, err := etcdv3.ConfigToClientv3(cfg)
	if err != nil {
		log.Fatal(err)
	}

	bDB, _ := etcdv3.NewEtcdConnectionWithBytes(*etcdConfig, log)
	dataBroker := kvproto.NewProtoWrapperWithSerializer(bDB, &keyval.SerializerJSON{}).
		NewBroker(configurator.ServiceLabel.GetAgentPrefix())

	time.Sleep(3 * time.Second)

	// This is how to use the Data Broker Txn API to create a new transaction. It is called from the HTTP handler
	// when a user triggers the creation of a new transaction via REST
	putTxn := dataBroker.NewTxn()
	for i := 1; i <= 3; i++ {
		exampleData1 := configurator.buildData("string", uint32(i), true)
		// putTxn.Put demonstrates how to use the Data Broker Txn Put() API. It is called from the HTTP handler
		// when a user invokes the REST API to add a new Put() operation to the transaction
		putTxn = putTxn.Put(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex+strconv.Itoa(i)), exampleData1)
	}
	// putTxn.Commit() demonstrates how to use the Data Broker Txn Commit() API. It is called from the HTTP handler
	// when a user invokes the REST API to commit a transaction.
	err = putTxn.Commit()
	if err != nil {
		log.Error(err)
	}
	// Another transaction chain to demonstrate delete operations. Put and Delete operations can be used together
	// within one transaction
	deleteTxn := dataBroker.NewTxn()
	for i := 1; i <= 3; i++ {
		// deleteTxn.Delete demonstrates how to use the Data Broker Txn Delete() API. It is called from the
		// HTTP handler when a user invokes the REST API to add a new Delete() operation to the transaction.
		// Put and Delete operations can be combined in the same transaction chain
		deleteTxn = deleteTxn.Delete(etcdKeyPrefixLabel(configurator.ServiceLabel.GetAgentLabel(), etcdIndex+strconv.Itoa(i)))
	}

	// Commit transactions to data store. Transaction executes multiple operations in a more efficient way in
	// contrast to executing them one by one.
	err = deleteTxn.Commit()
	if err != nil {
		log.Error(err)
	}
}

// The ETCD key prefix used for this example
func etcdKeyPrefix(agentLabel string) string {
	return "/vnf-agent/" + agentLabel + "/api/v1/example/db/simple/"
}

// The ETCD key (the key prefix + label)
func etcdKeyPrefixLabel(agentLabel string, index string) string {
	return etcdKeyPrefix(agentLabel) + index
}

/***********
 * KeyValProtoWatcher *
 ***********/

// Consumer (watcher) is subscribed to watch on data store changes. Change arrives via data change channel and
// its key is parsed
func (plugin *ExamplePlugin) consumer() {
	log.Print("KeyValProtoWatcher started")
	for {
		select {
		case dataChng := <-plugin.changeChannel:
			// If event arrives, the key is extracted and used together with the expected prefix to
			// identify item
			key := dataChng.GetKey()
			if strings.HasPrefix(key, etcdKeyPrefix(plugin.ServiceLabel.GetAgentLabel())) {
				var value, previousValue etcd_example.EtcdExample
				// The first return value is diff - boolean flag whether previous value exists or not
				err := dataChng.GetValue(&value)
				if err != nil {
					log.Error(err)
				}
				diff, err := dataChng.GetPrevValue(&previousValue)
				if err != nil {
					log.Error(err)
				}
				log.Infof("Event arrived to etcd eventHandler, key %v, update: %v, change type: %v,",
					dataChng.GetKey(), diff, dataChng.GetChangeType())
			}
			// Another strings.HasPrefix(key, etcd prefix) ...
		}
	}
}

// KeyValProtoWatcher is subscribed to data change channel and resync channel. ETCD transport adapter is used for this purpose
func (plugin *ExamplePlugin) subscribeWatcher() (err error) {
	plugin.watchDataReg, err = plugin.transport.
		Watch("Example etcd plugin", plugin.changeChannel, plugin.resyncChannel, etcdKeyPrefix(plugin.ServiceLabel.GetAgentLabel()))
	if err != nil {
		return err
	}

	log.Info("KeyValProtoWatcher subscribed")

	return nil
}

// Create simple ETCD data structure with provided data values
func (configurator *ExampleConfigurator) buildData(stringVal string, uint32Val uint32, boolVal bool) *etcd_example.EtcdExample {
	return &etcd_example.EtcdExample{
		StringVal: stringVal,
		Uint32Val: uint32Val,
		BoolVal:   boolVal,
	}
}
<|endoftext|>


<commit_before>// Package ocsp implements an OCSP responder based on a generic storage backend.
// It provides a couple of sample implementations.
// Because OCSP responders handle high query volumes, we have to be careful
// about how much logging we do. Error-level logs are reserved for problems
// internal to the server, that can be fixed by an administrator. Any type of
// incorrect input from a user should be logged and Info or below. For things
// that are logged on every request, Debug is the appropriate level.
package ocsp

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"time"

	"github.com/cloudflare/cfssl/certdb"
	"github.com/cloudflare/cfssl/log"
	"github.com/jmhodges/clock"
	"golang.org/x/crypto/ocsp"
)

var (
	malformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
	internalErrorErrorResponse    = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
	tryLaterErrorResponse         = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
	sigRequredErrorResponse       = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
	unauthorizedErrorResponse     = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
)

// Source represents the logical source of OCSP responses, i.e.,
// the logic that actually chooses a response based on a request.  In
// order to create an actual responder, wrap one of these in a Responder
// object and pass it to http.Handle.
type Source interface {
	Response(*ocsp.Request) ([]byte, bool)
}

// An InMemorySource is a map from serialNumber -> der(response)
type InMemorySource map[string][]byte

// Response looks up an OCSP response to provide for a given request.
// InMemorySource looks up a response purely based on serial number,
// without regard to what issuer the request is asking for.
func (src InMemorySource) Response(request *ocsp.Request) (response []byte, present bool) {
	response, present = src[request.SerialNumber.String()]
	return
}

// SqliteSource represnts a source of OCSP responses backed by certdb
type SqliteSource struct {
	Accessor certdb.Accessor
}

// NewSqliteSource creates a new SqliteSource type and associated dbAccessor
func NewSqliteSource(dbAccessor certdb.Accessor) Source {
	return SqliteSource{
		Accessor: dbAccessor,
	}
}

// Response implements cfssl.ocsp.responder.Source, returning the OCSP response
// with the expiration date furthest in the future
func (src SqliteSource) Response(req *ocsp.Request) ([]byte, bool) {
	if req == nil {
		return nil, false
	}

	// Extract the AKI string from req.IssuerKeyHash
	aki := hex.EncodeToString(req.IssuerKeyHash)

	sn := req.SerialNumber
	if sn == nil {
		return nil, false
	}
	strSN := sn.String()

	records, err := src.Accessor.GetOCSP(strSN, aki)

	// Log on errors obtaining OCSP response
	if err != nil {
		log.Errorf("Error obtaining OCSP response: %s", err)
	}

	// No record(s) to return
	if len(records) == 0 {
		return nil, false
	}

	// Find the OCSPRecord with the expiration date furthest in the future
	cur := records[0]
	for _, rec := range records {
		if rec.Expiry.After(cur.Expiry) {
			cur = rec
		}
	}
	return []byte(cur.Body), true
}

// NewSourceFromFile reads the named file into an InMemorySource.
// The file read by this function must contain whitespace-separated OCSP
// responses. Each OCSP response must be in base64-encoded DER form (i.e.,
// PEM without headers or whitespace).  Invalid responses are ignored.
// This function pulls the entire file into an InMemorySource.
func NewSourceFromFile(responseFile string) (Source, error) {
	fileContents, err := ioutil.ReadFile(responseFile)
	if err != nil {
		return nil, err
	}

	responsesB64 := regexp.MustCompile("\\s").Split(string(fileContents), -1)
	src := InMemorySource{}
	for _, b64 := range responsesB64 {
		// if the line/space is empty just skip
		if b64 == "" {
			continue
		}
		der, tmpErr := base64.StdEncoding.DecodeString(b64)
		if tmpErr != nil {
			log.Errorf("Base64 decode error %s on: %s", tmpErr, b64)
			continue
		}

		response, tmpErr := ocsp.ParseResponse(der, nil)
		if tmpErr != nil {
			log.Errorf("OCSP decode error %s on: %s", tmpErr, b64)
			continue
		}

		src[response.SerialNumber.String()] = der
	}

	log.Infof("Read %d OCSP responses", len(src))
	return src, nil
}

// A Responder object provides the HTTP logic to expose a
// Source of OCSP responses.
type Responder struct {
	Source Source
	clk    clock.Clock
}

// NewResponder instantiates a Responder with the give Source.
func NewResponder(source Source) *Responder {
	return &Responder{
		Source: source,
		clk:    clock.Default(),
	}
}

// A Responder can process both GET and POST requests.  The mapping
// from an OCSP request to an OCSP response is done by the Source;
// the Responder simply decodes the request, and passes back whatever
// response is provided by the source.
// Note: The caller must use http.StripPrefix to strip any path components
// (including '/') on GET requests.
// Do not use this responder in conjunction with http.NewServeMux, because the
// default handler will try to canonicalize path components by changing any
// strings of repeated '/' into a single '/', which will break the base64
// encoding.
func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	// By default we set a 'max-age=0, no-cache' Cache-Control header, this
	// is only returned to the client if a valid authorized OCSP response
	// is not found or an error is returned. If a response if found the header
	// will be altered to contain the proper max-age and modifiers.
	response.Header().Add("Cache-Control", "max-age=0, no-cache")
	// Read response from request
	var requestBody []byte
	var err error
	switch request.Method {
	case "GET":
		base64Request, err := url.QueryUnescape(request.URL.Path)
		if err != nil {
			log.Infof("Error decoding URL: %s", request.URL.Path)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
		// url.QueryUnescape not only unescapes %2B escaping, but it additionally
		// turns the resulting '+' into a space, which makes base64 decoding fail.
		// So we go back afterwards and turn ' ' back into '+'. This means we
		// accept some malformed input that includes ' ' or %20, but that's fine.
		base64RequestBytes := []byte(base64Request)
		for i := range base64RequestBytes {
			if base64RequestBytes[i] == ' ' {
				base64RequestBytes[i] = '+'
			}
		}
		requestBody, err = base64.StdEncoding.DecodeString(string(base64RequestBytes))
		if err != nil {
			log.Infof("Error decoding base64 from URL: %s", base64Request)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
	case "POST":
		requestBody, err = ioutil.ReadAll(request.Body)
		if err != nil {
			log.Errorf("Problem reading body of POST: %s", err)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
	default:
		response.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	b64Body := base64.StdEncoding.EncodeToString(requestBody)
	log.Debugf("Received OCSP request: %s", b64Body)

	// All responses after this point will be OCSP.
	// We could check for the content type of the request, but that
	// seems unnecessariliy restrictive.
	response.Header().Add("Content-Type", "application/ocsp-response")

	// Parse response as an OCSP request
	// XXX: This fails if the request contains the nonce extension.
	//      We don't intend to support nonces anyway, but maybe we
	//      should return unauthorizedRequest instead of malformed.
	ocspRequest, err := ocsp.ParseRequest(requestBody)
	if err != nil {
		log.Infof("Error decoding request body: %s", b64Body)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(malformedRequestErrorResponse)
		return
	}

	// Look up OCSP response from source
	ocspResponse, found := rs.Source.Response(ocspRequest)
	if !found {
		log.Infof("No response found for request: serial %x, request body %s",
			ocspRequest.SerialNumber, b64Body)
		response.Write(unauthorizedErrorResponse)
		return
	}

	parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil)
	if err != nil {
		log.Errorf("Error parsing response for serial %x: %s",
			ocspRequest.SerialNumber, err)
		response.Write(unauthorizedErrorResponse)
		return
	}

	// Write OCSP response to response
	response.Header().Add("Last-Modified", parsedResponse.ThisUpdate.Format(time.RFC1123))
	response.Header().Add("Expires", parsedResponse.NextUpdate.Format(time.RFC1123))
	now := rs.clk.Now()
	maxAge := 0
	if now.Before(parsedResponse.NextUpdate) {
		maxAge = int(parsedResponse.NextUpdate.Sub(now) / time.Second)
	} else {
		// TODO(#530): we want max-age=0 but this is technically an authorized OCSP response
		//             (despite being stale) and 5019 forbids attaching no-cache
		maxAge = 0
	}
	response.Header().Set(
		"Cache-Control",
		fmt.Sprintf(
			"max-age=%d, public, no-transform, must-revalidate",
			maxAge,
		),
	)
	responseHash := sha256.Sum256(ocspResponse)
	response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))

	// RFC 7232 says that a 304 response must contain the above
	// headers if they would also be sent for a 200 for the same
	// request, so we have to wait until here to do this
	if etag := request.Header.Get("If-None-Match"); etag != "" {
		if etag == fmt.Sprintf("\"%X\"", responseHash) {
			response.WriteHeader(http.StatusNotModified)
			return
		}
	}
	response.WriteHeader(http.StatusOK)
	response.Write(ocspResponse)
}
<commit_msg>fix weird gofmt issue<commit_after>// Package ocsp implements an OCSP responder based on a generic storage backend.
// It provides a couple of sample implementations.
// Because OCSP responders handle high query volumes, we have to be careful
// about how much logging we do. Error-level logs are reserved for problems
// internal to the server, that can be fixed by an administrator. Any type of
// incorrect input from a user should be logged and Info or below. For things
// that are logged on every request, Debug is the appropriate level.
package ocsp

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"time"

	"github.com/cloudflare/cfssl/certdb"
	"github.com/cloudflare/cfssl/log"
	"github.com/jmhodges/clock"
	"golang.org/x/crypto/ocsp"
)

var (
	malformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
	internalErrorErrorResponse    = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
	tryLaterErrorResponse         = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
	sigRequredErrorResponse       = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
	unauthorizedErrorResponse     = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
)

// Source represents the logical source of OCSP responses, i.e.,
// the logic that actually chooses a response based on a request.  In
// order to create an actual responder, wrap one of these in a Responder
// object and pass it to http.Handle.
type Source interface {
	Response(*ocsp.Request) ([]byte, bool)
}

// An InMemorySource is a map from serialNumber -> der(response)
type InMemorySource map[string][]byte

// Response looks up an OCSP response to provide for a given request.
// InMemorySource looks up a response purely based on serial number,
// without regard to what issuer the request is asking for.
func (src InMemorySource) Response(request *ocsp.Request) (response []byte, present bool) {
	response, present = src[request.SerialNumber.String()]
	return
}

// SqliteSource represnts a source of OCSP responses backed by certdb
type SqliteSource struct {
	Accessor certdb.Accessor
}

// NewSqliteSource creates a new SqliteSource type and associated dbAccessor
func NewSqliteSource(dbAccessor certdb.Accessor) Source {
	return SqliteSource{
		Accessor: dbAccessor,
	}
}

// Response implements cfssl.ocsp.responder.Source, returning the OCSP response
// with the expiration date furthest in the future
func (src SqliteSource) Response(req *ocsp.Request) ([]byte, bool) {
	if req == nil {
		return nil, false
	}

	// Extract the AKI string from req.IssuerKeyHash
	aki := hex.EncodeToString(req.IssuerKeyHash)

	sn := req.SerialNumber
	if sn == nil {
		return nil, false
	}
	strSN := sn.String()

	records, err := src.Accessor.GetOCSP(strSN, aki)

	// Log on errors obtaining OCSP response
	if err != nil {
		log.Errorf("Error obtaining OCSP response: %s", err)
	}

	if len(records) == 0 {
		return nil, false
	}

	// Find the OCSPRecord with the expiration date furthest in the future
	cur := records[0]
	for _, rec := range records {
		if rec.Expiry.After(cur.Expiry) {
			cur = rec
		}
	}
	return []byte(cur.Body), true
}

// NewSourceFromFile reads the named file into an InMemorySource.
// The file read by this function must contain whitespace-separated OCSP
// responses. Each OCSP response must be in base64-encoded DER form (i.e.,
// PEM without headers or whitespace).  Invalid responses are ignored.
// This function pulls the entire file into an InMemorySource.
func NewSourceFromFile(responseFile string) (Source, error) {
	fileContents, err := ioutil.ReadFile(responseFile)
	if err != nil {
		return nil, err
	}

	responsesB64 := regexp.MustCompile("\\s").Split(string(fileContents), -1)
	src := InMemorySource{}
	for _, b64 := range responsesB64 {
		// if the line/space is empty just skip
		if b64 == "" {
			continue
		}
		der, tmpErr := base64.StdEncoding.DecodeString(b64)
		if tmpErr != nil {
			log.Errorf("Base64 decode error %s on: %s", tmpErr, b64)
			continue
		}

		response, tmpErr := ocsp.ParseResponse(der, nil)
		if tmpErr != nil {
			log.Errorf("OCSP decode error %s on: %s", tmpErr, b64)
			continue
		}

		src[response.SerialNumber.String()] = der
	}

	log.Infof("Read %d OCSP responses", len(src))
	return src, nil
}

// A Responder object provides the HTTP logic to expose a
// Source of OCSP responses.
type Responder struct {
	Source Source
	clk    clock.Clock
}

// NewResponder instantiates a Responder with the give Source.
func NewResponder(source Source) *Responder {
	return &Responder{
		Source: source,
		clk:    clock.Default(),
	}
}

// A Responder can process both GET and POST requests.  The mapping
// from an OCSP request to an OCSP response is done by the Source;
// the Responder simply decodes the request, and passes back whatever
// response is provided by the source.
// Note: The caller must use http.StripPrefix to strip any path components
// (including '/') on GET requests.
// Do not use this responder in conjunction with http.NewServeMux, because the
// default handler will try to canonicalize path components by changing any
// strings of repeated '/' into a single '/', which will break the base64
// encoding.
func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	// By default we set a 'max-age=0, no-cache' Cache-Control header, this
	// is only returned to the client if a valid authorized OCSP response
	// is not found or an error is returned. If a response if found the header
	// will be altered to contain the proper max-age and modifiers.
	response.Header().Add("Cache-Control", "max-age=0, no-cache")
	// Read response from request
	var requestBody []byte
	var err error
	switch request.Method {
	case "GET":
		base64Request, err := url.QueryUnescape(request.URL.Path)
		if err != nil {
			log.Infof("Error decoding URL: %s", request.URL.Path)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
		// url.QueryUnescape not only unescapes %2B escaping, but it additionally
		// turns the resulting '+' into a space, which makes base64 decoding fail.
		// So we go back afterwards and turn ' ' back into '+'. This means we
		// accept some malformed input that includes ' ' or %20, but that's fine.
		base64RequestBytes := []byte(base64Request)
		for i := range base64RequestBytes {
			if base64RequestBytes[i] == ' ' {
				base64RequestBytes[i] = '+'
			}
		}
		requestBody, err = base64.StdEncoding.DecodeString(string(base64RequestBytes))
		if err != nil {
			log.Infof("Error decoding base64 from URL: %s", base64Request)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
	case "POST":
		requestBody, err = ioutil.ReadAll(request.Body)
		if err != nil {
			log.Errorf("Problem reading body of POST: %s", err)
			response.WriteHeader(http.StatusBadRequest)
			return
		}
	default:
		response.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	b64Body := base64.StdEncoding.EncodeToString(requestBody)
	log.Debugf("Received OCSP request: %s", b64Body)

	// All responses after this point will be OCSP.
	// We could check for the content type of the request, but that
	// seems unnecessariliy restrictive.
	response.Header().Add("Content-Type", "application/ocsp-response")

	// Parse response as an OCSP request
	// XXX: This fails if the request contains the nonce extension.
	//      We don't intend to support nonces anyway, but maybe we
	//      should return unauthorizedRequest instead of malformed.
	ocspRequest, err := ocsp.ParseRequest(requestBody)
	if err != nil {
		log.Infof("Error decoding request body: %s", b64Body)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(malformedRequestErrorResponse)
		return
	}

	// Look up OCSP response from source
	ocspResponse, found := rs.Source.Response(ocspRequest)
	if !found {
		log.Infof("No response found for request: serial %x, request body %s",
			ocspRequest.SerialNumber, b64Body)
		response.Write(unauthorizedErrorResponse)
		return
	}

	parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil)
	if err != nil {
		log.Errorf("Error parsing response for serial %x: %s",
			ocspRequest.SerialNumber, err)
		response.Write(unauthorizedErrorResponse)
		return
	}

	// Write OCSP response to response
	response.Header().Add("Last-Modified", parsedResponse.ThisUpdate.Format(time.RFC1123))
	response.Header().Add("Expires", parsedResponse.NextUpdate.Format(time.RFC1123))
	now := rs.clk.Now()
	maxAge := 0
	if now.Before(parsedResponse.NextUpdate) {
		maxAge = int(parsedResponse.NextUpdate.Sub(now) / time.Second)
	} else {
		// TODO(#530): we want max-age=0 but this is technically an authorized OCSP response
		//             (despite being stale) and 5019 forbids attaching no-cache
		maxAge = 0
	}
	response.Header().Set(
		"Cache-Control",
		fmt.Sprintf(
			"max-age=%d, public, no-transform, must-revalidate",
			maxAge,
		),
	)
	responseHash := sha256.Sum256(ocspResponse)
	response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))

	// RFC 7232 says that a 304 response must contain the above
	// headers if they would also be sent for a 200 for the same
	// request, so we have to wait until here to do this
	if etag := request.Header.Get("If-None-Match"); etag != "" {
		if etag == fmt.Sprintf("\"%X\"", responseHash) {
			response.WriteHeader(http.StatusNotModified)
			return
		}
	}
	response.WriteHeader(http.StatusOK)
	response.Write(ocspResponse)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"sync"

	"github.com/golang/glog"
)

type OffsetMap map[string]map[int32]int64

type OffsetManager struct {
	*StartStopControl

	// config
	config OffsetMangerConfig

	// parent
	manager *CallbackManager

	// master storage
	offsetStorage OffsetStorage

	// slave storage
	slaveOffsetStorage map[string]OffsetStorage

	// offsetStorage runner
	offsetStorageRunner *ServiceRunner

	// lastCommitted offset
	lastCommitted OffsetMap
	l             sync.RWMutex
}

func NewOffsetManager() *OffsetManager {
	return &OffsetManager{
		StartStopControl: &StartStopControl{},
	}
}

func (om *OffsetManager) Init(config OffsetMangerConfig, manager *CallbackManager) error {
	// init storage/slaveStorage
	om.config = config
	om.manager = manager
	om.lastCommitted = make(OffsetMap)

	var err error

	if om.offsetStorage, err = NewOffsetStorage(om.config.StorageName); err != nil {
		return err
	}

	if err = om.offsetStorage.Init(om.config.StorageConfig, om.manager); err != nil {
		return err
	}

	om.slaveOffsetStorage = make(map[string]OffsetStorage)

	for storageName, storageConfig := range om.config.SlaveStorage {
		var storage OffsetStorage
		if storage, err = NewOffsetStorage(storageName); err != nil {
			return err
		}
		if err = storage.Init(storageConfig, om.manager); err != nil {
			return err
		}
		om.slaveOffsetStorage[storageName] = storage
	}

	om.offsetStorageRunner = NewServiceRunner()

	return nil
}

func (om *OffsetManager) InitializePartition(topic string, partition int32) (int64, error) {
	om.l.Lock()
	defer om.l.Unlock()

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if _, err := storage.InitializePartition(topic, partition); err != nil {
				glog.Warningf("Initialize slaveOffsetStorage failed [topic:%s][partition:%d][storageName:%s]",
					topic, partition, name)
			}
		}(name, storage)
	}

	return om.offsetStorage.InitializePartition(topic, partition)
}

func (om *OffsetManager) FinalizePartition(topic string, partition int32) error {
	om.l.Lock()
	defer om.l.Unlock()
	defer func() {
		if om.lastCommitted[topic] != nil {
			if _, exists := om.lastCommitted[topic][partition]; !exists {
				delete(om.lastCommitted[topic], partition)
			}
		}
	}()

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if err := storage.FinalizePartition(topic, partition); err != nil {
				glog.Warningf("Finalize slaveOffsetStorage failed [topic:%s][partition:%d][storageName:%s]",
					topic, partition, name)
			}
		}(name, storage)
	}

	return om.offsetStorage.FinalizePartition(topic, partition)
}

func (om *OffsetManager) GetOffsets() OffsetMap {
	om.l.RLock()
	defer om.l.RUnlock()

	result := make(OffsetMap)

	for topic, partitionOffsets := range om.lastCommitted {
		resultOffsets := make(map[int32]int64)

		for partition, offset := range partitionOffsets {
			resultOffsets[partition] = offset
		}

		result[topic] = resultOffsets
	}

	return result
}

func (om *OffsetManager) CommitOffset(topic string, partition int32, offset int64) error {
	om.l.Lock()
	defer om.l.Unlock()

	if om.lastCommitted[topic] == nil {
		om.lastCommitted[topic] = make(map[int32]int64)
	}

	if om.lastCommitted[topic][partition] > offset {
		glog.Errorf("Invalid offset committed [topic:%s][partition:%d][lastCommitted:%d][current:%d]",
			topic, partition, om.lastCommitted[topic][partition], offset)
		return errors.New("Invalid offset to commit")
	}

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if err := storage.WriteOffset(topic, partition, offset+1); err != nil {
				glog.Warningf("CommitOffset to slaveOffsetStorage failed [topic:%s][partition:%d][offset:%d][storageName:%s]",
					topic, partition, offset, name)
			}
		}(name, storage)
	}

	if err := om.offsetStorage.WriteOffset(topic, partition, offset+1); err == nil {
		// update offset to lastCommitted
		om.lastCommitted[topic][partition] = offset
	} else {
		return err
	}

	return nil
}

func (om *OffsetManager) Run() error {
	if err := om.ensureStart(); err != nil {
		return err
	}
	defer om.markStop()

	// start all offsetStorage run
	offsetStorage := make([]Runnable, 0, len(om.slaveOffsetStorage)+1)

	offsetStorage = append(offsetStorage, om.offsetStorage)

	for _, storage := range om.slaveOffsetStorage {
		offsetStorage = append(offsetStorage, storage)
	}

	om.offsetStorageRunner.Prepare()
	if _, err := om.offsetStorageRunner.RunAsync(offsetStorage); err != nil {
		return err
	}
	defer om.offsetStorageRunner.Close()

	select {
	case <-om.offsetStorageRunner.WaitForExitChannel():
	case <-om.WaitForCloseChannel():
	}

	return nil
}
<commit_msg>Set default partition offset to -1 for nocommit initialized partitions<commit_after>package main

import (
	"errors"
	"sync"

	"github.com/golang/glog"
)

type OffsetMap map[string]map[int32]int64

type OffsetManager struct {
	*StartStopControl

	// config
	config OffsetMangerConfig

	// parent
	manager *CallbackManager

	// master storage
	offsetStorage OffsetStorage

	// slave storage
	slaveOffsetStorage map[string]OffsetStorage

	// offsetStorage runner
	offsetStorageRunner *ServiceRunner

	// lastCommitted offset
	lastCommitted OffsetMap
	l             sync.RWMutex
}

func NewOffsetManager() *OffsetManager {
	return &OffsetManager{
		StartStopControl: &StartStopControl{},
	}
}

func (om *OffsetManager) Init(config OffsetMangerConfig, manager *CallbackManager) error {
	// init storage/slaveStorage
	om.config = config
	om.manager = manager
	om.lastCommitted = make(OffsetMap)

	var err error

	if om.offsetStorage, err = NewOffsetStorage(om.config.StorageName); err != nil {
		return err
	}

	if err = om.offsetStorage.Init(om.config.StorageConfig, om.manager); err != nil {
		return err
	}

	om.slaveOffsetStorage = make(map[string]OffsetStorage)

	for storageName, storageConfig := range om.config.SlaveStorage {
		var storage OffsetStorage
		if storage, err = NewOffsetStorage(storageName); err != nil {
			return err
		}
		if err = storage.Init(storageConfig, om.manager); err != nil {
			return err
		}
		om.slaveOffsetStorage[storageName] = storage
	}

	om.offsetStorageRunner = NewServiceRunner()

	return nil
}

func (om *OffsetManager) InitializePartition(topic string, partition int32) (int64, error) {
	om.l.Lock()
	defer om.l.Unlock()

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if _, err := storage.InitializePartition(topic, partition); err != nil {
				glog.Warningf("Initialize slaveOffsetStorage failed [topic:%s][partition:%d][storageName:%s]",
					topic, partition, name)
			}
		}(name, storage)
	}

	offset, err := om.offsetStorage.InitializePartition(topic, partition)

	if err == nil {
		if om.lastCommitted[topic] == nil {
			om.lastCommitted[topic] = make(map[int32]int64)
		}

		om.lastCommitted[topic][partition] = -1
	}

	return offset, err
}

func (om *OffsetManager) FinalizePartition(topic string, partition int32) error {
	om.l.Lock()
	defer om.l.Unlock()
	defer func() {
		if om.lastCommitted[topic] != nil {
			if _, exists := om.lastCommitted[topic][partition]; !exists {
				delete(om.lastCommitted[topic], partition)
			}
		}
	}()

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if err := storage.FinalizePartition(topic, partition); err != nil {
				glog.Warningf("Finalize slaveOffsetStorage failed [topic:%s][partition:%d][storageName:%s]",
					topic, partition, name)
			}
		}(name, storage)
	}

	return om.offsetStorage.FinalizePartition(topic, partition)
}

func (om *OffsetManager) GetOffsets() OffsetMap {
	om.l.RLock()
	defer om.l.RUnlock()

	result := make(OffsetMap)

	for topic, partitionOffsets := range om.lastCommitted {
		resultOffsets := make(map[int32]int64)

		for partition, offset := range partitionOffsets {
			resultOffsets[partition] = offset
		}

		result[topic] = resultOffsets
	}

	return result
}

func (om *OffsetManager) CommitOffset(topic string, partition int32, offset int64) error {
	om.l.Lock()
	defer om.l.Unlock()

	if om.lastCommitted[topic] == nil {
		om.lastCommitted[topic] = make(map[int32]int64)
	}

	if om.lastCommitted[topic][partition] > offset {
		glog.Errorf("Invalid offset committed [topic:%s][partition:%d][lastCommitted:%d][current:%d]",
			topic, partition, om.lastCommitted[topic][partition], offset)
		return errors.New("Invalid offset to commit")
	}

	for name, storage := range om.slaveOffsetStorage {
		go func(name string, storage OffsetStorage) {
			if err := storage.WriteOffset(topic, partition, offset+1); err != nil {
				glog.Warningf("CommitOffset to slaveOffsetStorage failed [topic:%s][partition:%d][offset:%d][storageName:%s]",
					topic, partition, offset, name)
			}
		}(name, storage)
	}

	if err := om.offsetStorage.WriteOffset(topic, partition, offset+1); err == nil {
		// update offset to lastCommitted
		om.lastCommitted[topic][partition] = offset
	} else {
		return err
	}

	return nil
}

func (om *OffsetManager) Run() error {
	if err := om.ensureStart(); err != nil {
		return err
	}
	defer om.markStop()

	// start all offsetStorage run
	offsetStorage := make([]Runnable, 0, len(om.slaveOffsetStorage)+1)

	offsetStorage = append(offsetStorage, om.offsetStorage)

	for _, storage := range om.slaveOffsetStorage {
		offsetStorage = append(offsetStorage, storage)
	}

	om.offsetStorageRunner.Prepare()
	if _, err := om.offsetStorageRunner.RunAsync(offsetStorage); err != nil {
		return err
	}
	defer om.offsetStorageRunner.Close()

	select {
	case <-om.offsetStorageRunner.WaitForExitChannel():
	case <-om.WaitForCloseChannel():
	}

	return nil
}
<|endoftext|>


<commit_before>// API Version 1
package main

import (
	"encoding/json"
	//"encoding/base64"
	"github.com/labstack/echo"
	"github.com/tmaiaroto/discfg/commands"
	"github.com/tmaiaroto/discfg/config"
	//"github.com/ugorji/go/codec" // <-- may eventually be used as a different output format. have to see what echo supports.
	//"log"
	"net/http"
	//	"strconv"
	"io/ioutil"
)

// Set the routes for V1 API
func v1Routes(e *echo.Echo) {
	e.Put("/v1/:name/keys/:key", v1SetKey)
	e.Get("/v1/:name/keys/:key", v1GetKey)
	e.Delete("/v1/:name/keys/:key", v1DeleteKey)

	e.Put("/v1/create/:name", v1CreateCfg)
}

// Gets a key from discfg
func v1GetKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	resp := commands.GetKey(Options)
	// Since this option is not needed for anything else, it's not held on the Options struct.
	contentType := c.Query("type")

	// This is very awesome. Very interesting possibilties now.
	switch contentType {
	case "text", "text/plain", "string":
		return c.String(http.StatusOK, string(resp.Node.Value.([]byte)))
		break
	// This one is going to be interesting. Weird? Bad practice? I don't know, but I dig it and it starts giving me wild ideas.
	case "html", "text/html":
		return c.HTML(http.StatusOK, string(resp.Node.Value.([]byte)))
		break
	// TODO:
	//case "jsonp":
	//break
	case "json", "application/json":
		resp = formatJsonValue(resp)
		break
	default:
		resp = formatJsonValue(resp)
		break
	}
	// default response
	return c.JSON(http.StatusOK, resp)
}

// Sets the Node Value (an interface{}) as a map[string]interface{} (from []byte which is how it's stored - at least for now)
// so that it can be converted to JSON in the Echo response. If it can't be represented in a map, then it'll be set as a string.
// For example, a string was set as the value, we can still represent that in JSON. However, if an image was stored...Then it's
// going to look ugly. It won't be a base64 string, it'll be the string representation of the binary data. Which apparently Chrome
// will render if given...But still. Not so hot. The user should know what they are setting and getting though and this should
// still technically return JSON with a usable value. Valid JSON at that. Just with some funny looking characters =)
func formatJsonValue(resp config.ResponseObject) config.ResponseObject {
	// Don't attempt to Unmarshal or anything if the Value is empty. We wouldn't want to create a panic now.
	if resp.Node.Value == nil {
		return resp
	}
	var dat map[string]interface{}
	if err := json.Unmarshal(resp.Node.Value.([]byte), &dat); err != nil {
		resp.Node.Value = string(resp.Node.Value.([]byte))
	} else {
		resp.Node.Value = dat
	}
	return resp
}

// Sets a key in discfg
func v1SetKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	resp := config.ResponseObject{
		Action: "set",
	}

	// Allow the value to be passed via querystring param.
	Options.Value = []byte(c.Query("value"))

	// Overwrite that if the request body passes a value that can be read, preferring that.
	b, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		// If reading the body failed and we don't have a value from the querystring parameter
		// then we have a (potential) problem.
		//
		// Some data stores may be ok with an empty key value. DynamoDB is not. It will only
		// return a ValidationException error. Plus, even if it was allowed, it would really
		// confuse the user. Some random error reading the body of a request and poof, the data
		// vanishes? That'd be terrible UX.
		// log.Println(err)
		resp.Error = err.Error()
		resp.Message = "Something went wrong reading the body of the request."
		// resp.ErrorCode = 500 <-- TODO: I need to come up with discfg specific error codes. Keep as const somewhere.
		//	return c.JSON(http.StatusOK, resp)
		//	Or maybe return an HTTP status message... This is outside discfg's concern. It's not an error message/code
		//	that would ever be seen from the CLI, right? Or maybe it would. Maybe a more generic, "error parsing key value" ...
	} else if len(b) > 0 {
		Options.Value = b
	}

	resp = commands.SetKey(Options)

	return c.JSON(http.StatusOK, resp)
}

// Deletes a key in discfg
func v1DeleteKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	resp := commands.DeleteKey(Options)

	return c.JSON(http.StatusOK, resp)
}

func v1CreateCfg(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	return c.JSON(http.StatusOK, commands.CreateCfg(Options))
}
<commit_msg>minor refactor<commit_after>// API Version 1
package main

import (
	"encoding/json"
	//"encoding/base64"
	"github.com/labstack/echo"
	"github.com/tmaiaroto/discfg/commands"
	"github.com/tmaiaroto/discfg/config"
	//"github.com/ugorji/go/codec" // <-- may eventually be used as a different output format. have to see what echo supports.
	//"log"
	"net/http"
	//	"strconv"
	"io/ioutil"
)

// Set the routes for V1 API
func v1Routes(e *echo.Echo) {
	e.Put("/v1/:name/keys/:key", v1SetKey)
	e.Get("/v1/:name/keys/:key", v1GetKey)
	e.Delete("/v1/:name/keys/:key", v1DeleteKey)

	e.Put("/v1/create/:name", v1CreateCfg)
}

// Gets a key from discfg
func v1GetKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	resp := commands.GetKey(Options)
	// Since this option is not needed for anything else, it's not held on the Options struct.
	contentType := c.Query("type")

	// This is very awesome. Very interesting possibilties now.
	switch contentType {
	case "text", "text/plain", "string":
		return c.String(http.StatusOK, string(resp.Node.Value.([]byte)))
		break
	// This one is going to be interesting. Weird? Bad practice? I don't know, but I dig it and it starts giving me wild ideas.
	case "html", "text/html":
		return c.HTML(http.StatusOK, string(resp.Node.Value.([]byte)))
		break
	// TODO:
	//case "jsonp":
	//break
	case "json", "application/json":
		resp = formatJsonValue(resp)
		break
	default:
		resp = formatJsonValue(resp)
		break
	}
	// default response
	return c.JSON(http.StatusOK, resp)
}

// Sets the Node Value (an interface{}) as a map[string]interface{} (from []byte which is how it's stored - at least for now)
// so that it can be converted to JSON in the Echo response. If it can't be represented in a map, then it'll be set as a string.
// For example, a string was set as the value, we can still represent that in JSON. However, if an image was stored...Then it's
// going to look ugly. It won't be a base64 string, it'll be the string representation of the binary data. Which apparently Chrome
// will render if given...But still. Not so hot. The user should know what they are setting and getting though and this should
// still technically return JSON with a usable value. Valid JSON at that. Just with some funny looking characters =)
func formatJsonValue(resp config.ResponseObject) config.ResponseObject {
	// Don't attempt to Unmarshal or anything if the Value is empty. We wouldn't want to create a panic now.
	if resp.Node.Value == nil {
		return resp
	}
	var dat map[string]interface{}
	if err := json.Unmarshal(resp.Node.Value.([]byte), &dat); err != nil {
		resp.Node.Value = string(resp.Node.Value.([]byte))
	} else {
		resp.Node.Value = dat
	}
	return resp
}

// Sets a key in discfg
func v1SetKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	resp := config.ResponseObject{
		Action: "set",
	}

	// Allow the value to be passed via querystring param.
	Options.Value = []byte(c.Query("value"))

	// Overwrite that if the request body passes a value that can be read, preferring that.
	b, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		// If reading the body failed and we don't have a value from the querystring parameter
		// then we have a (potential) problem.
		//
		// Some data stores may be ok with an empty key value. DynamoDB is not. It will only
		// return a ValidationException error. Plus, even if it was allowed, it would really
		// confuse the user. Some random error reading the body of a request and poof, the data
		// vanishes? That'd be terrible UX.
		// log.Println(err)
		resp.Error = err.Error()
		resp.Message = "Something went wrong reading the body of the request."
		// resp.ErrorCode = 500 <-- TODO: I need to come up with discfg specific error codes. Keep as const somewhere.
		//	return c.JSON(http.StatusOK, resp)
		//	Or maybe return an HTTP status message... This is outside discfg's concern. It's not an error message/code
		//	that would ever be seen from the CLI, right? Or maybe it would. Maybe a more generic, "error parsing key value" ...
	} else if len(b) > 0 {
		Options.Value = b
	}

	resp = commands.SetKey(Options)

	return c.JSON(http.StatusOK, resp)
}

// Deletes a key in discfg
func v1DeleteKey(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	Options.Key = c.Param("key")
	return c.JSON(http.StatusOK, commands.DeleteKey(Options))
}

func v1CreateCfg(c *echo.Context) error {
	Options.CfgName = c.Param("name")
	return c.JSON(http.StatusOK, commands.CreateCfg(Options))
}
<|endoftext|>


<commit_before>// http://cryptopals.com/sets/1/
package cryptopals

import (
	"io/ioutil"
	"net/http"
	"strings"
	"testing"

	"github.com/ianferguson/cryptopals/encodings"
	"github.com/ianferguson/cryptopals/vigenere"
)

// Challenge 1 converts a provided hex string to binary and then from binary to base64, verifying its result
// http://cryptopals.com/sets/1/challenges/1/
func TestSet1Challenge1(test *testing.T) {
	input := "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"
	output := "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"
	bytes := encodings.HexToBytes(input)
	result := encodings.BytesToBase64(bytes)

	if result != output {
		test.Errorf("base64 encoded string was %v but %v was expected", result, input)
	}

}

// Challenge 2 takes tow inputs and xor's them together
// http://cryptopals.com/sets/1/challenges/2/
func TestSet1Challenge2(test *testing.T) {
	input1 := "1c0111001f010100061a024b53535009181c"
	input2 := "686974207468652062756c6c277320657965"
	expected := "746865206b696420646f6e277420706c6179"
	b1 := encodings.HexToBytes(input1)
	b2 := encodings.HexToBytes(input2)
	result := encodings.BytesToHex(xor(b1, b2))
	if result != expected {
		test.Errorf("expected %v but got %v", expected, result)
	}
}

// Challenge 3 is to crack a hex string that has been xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/3/
func TestSet1Challenge3(test *testing.T) {
	hexInput := "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
	output := vigenere.CrackSimple(encodings.HexToBytes(hexInput))
	expected := "Cooking MC's like a pound of bacon"
	if output.Text != expected {
		test.Errorf("expected the input to decode to %v but got %q instead", expected, output.Text)
	}

	if output.Key != 'X' {
		test.Errorf("expected the key to be guessed as 'X' but was %q", output.Key)
	}
}

// Challenge 4 is to locate the 1 string within 60 hex snippets that is a
// an english string xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/4/
func TestSet1Challenge4(test *testing.T) {
	text := get("http://cryptopals.com/static/challenge-data/4.txt")
	expected := "Now that the party is jumping\n"
	// BUG(ian) assumes that 1 and only 1 string in the input text, should probably accumulate all
	// qualifying strings in a slice
	var found string
	for _, code := range strings.Split(text, "\n") {
		s := vigenere.CrackSimple(encodings.HexToBytes(code))
		if s.Score < 0.025 {
			found = s.Text
		}
	}

	if found != expected {
		test.Errorf("expected clear text to be found with message %v but found %q", expected, found)
	}
}

// Challenge 5 is to implement a function that applies a multi byte key to a plaintext cyclically
// http://cryptopals.com/sets/1/challenges/5/
func TestSet1Challenge5(test *testing.T) {
	input := "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"
	expected := "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272" +
		"a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f"
	key := []byte("ICE")
	ciphertext := vigenere.Encrypt([]byte(input), key)
	hextext := encodings.BytesToHex(ciphertext)
	if hextext != expected {
		test.Errorf("Expected %v but got %v", expected, hextext)
	}

}

// Challenge 6 requires detecting the key length of a plaintext xor'd
// with a fixed key, and then cracking that key
// http://cryptopals.com/sets/1/challenges/6/
func TestSet1Challenge6(test *testing.T) {
	ciphertext := get("http://cryptopals.com/static/challenge-data/6.txt")
	expected := "I'm back and I'm ringin' the bell \n" +
		"A rockin' on the mike while the fly girls yell \n" +
		"In ecstasy in the back of me \n" +
		"Well that's my DJ Deshay cuttin' all them Z's \n" +
		"Hittin' hard and the girlies goin' crazy \n" +
		"Vanilla's on the mike, man I'm not lazy. \n" +
		"\n" +
		"I'm lettin' my drug kick in \n" +
		"It controls my mouth and I begin \n" +
		"To just let it flow, let my concepts go \n" +
		"My posse's to the side yellin', Go Vanilla Go! \n" +
		"\n" +
		"Smooth 'cause that's the way I will be \n" +
		"And if you don't give a damn, then \n" +
		"Why you starin' at me \n" +
		"So get off 'cause I control the stage \n" +
		"There's no dissin' allowed \n" +
		"I'm in my own phase \n" +
		"The girlies sa y they love me and that is ok \n" +
		"And I can dance better than any kid n' play \n" +
		"\n" +
		"Stage 2 -- Yea the one ya' wanna listen to \n" +
		"It's off my head so let the beat play through \n" +
		"So I can funk it up and make it sound good \n" +
		"1-2-3 Yo -- Knock on some wood \n" +
		"For good luck, I like my rhymes atrocious \n" +
		"Supercalafragilisticexpialidocious \n" +
		"I'm an effect and that you can bet \n" +
		"I can take a fly girl and make her wet. \n" +
		"\n" +
		"I'm like Samson -- Samson to Delilah \n" +
		"There's no denyin', You can try to hang \n" +
		"But you'll keep tryin' to get my style \n" +
		"Over and over, practice makes perfect \n" +
		"But not if you're a loafer. \n" +
		"\n" +
		"You'll get nowhere, no place, no time, no girls \n" +
		"Soon -- Oh my God, homebody, you probably eat \n" +
		"Spaghetti with a spoon! Come on and say it! \n" +
		"\n" +
		"VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino \n" +
		"Intoxicating so you stagger like a wino \n" +
		"So punks stop trying and girl stop cryin' \n" +
		"Vanilla Ice is sellin' and you people are buyin' \n" +
		"'Cause why the freaks are jockin' like Crazy Glue \n" +
		"Movin' and groovin' trying to sing along \n" +
		"All through the ghetto groovin' this here song \n" +
		"Now you're amazed by the VIP posse. \n" +
		"\n" +
		"Steppin' so hard like a German Nazi \n" +
		"Startled by the bases hittin' ground \n" +
		"There's no trippin' on mine, I'm just gettin' down \n" +
		"Sparkamatic, I'm hangin' tight like a fanatic \n" +
		"You trapped me once and I thought that \n" +
		"You might have it \n" +
		"So step down and lend me your ear \n" +
		"'89 in my time! You, '90 is my year. \n" +
		"\n" +
		"You're weakenin' fast, YO! and I can tell it \n" +
		"Your body's gettin' hot, so, so I can smell it \n" +
		"So don't be mad and don't be sad \n" +
		"'Cause the lyrics belong to ICE, You can call me Dad \n" +
		"You're pitchin' a fit, so step back and endure \n" +
		"Let the witch doctor, Ice, do the dance to cure \n" +
		"So come up close and don't be square \n" +
		"You wanna battle me -- Anytime, anywhere \n" +
		"\n" +
		"You thought that I was weak, Boy, you're dead wrong \n" +
		"So come on, everybody and sing this song \n" +
		"\n" +
		"Say -- Play that funky music Say, go white boy, go white boy go \n" +
		"play that funky music Go white boy, go white boy, go \n" +
		"Lay down and boogie and play that funky music till you die. \n" +
		"\n" +
		"Play that funky music Come on, Come on, let me hear \n" +
		"Play that funky music white boy you say it, say it \n" +
		"Play that funky music A little louder now \n" +
		"Play that funky music, white boy Come on, Come on, Come on \n" +
		"Play that funky music \n"
	p := string(vigenere.Crack(encodings.Base64ToBytes(ciphertext)))
	if p != expected {
		test.Errorf("expected: %v\nbut got: %q", expected, p)
	}
}

// wrapper for getting a plaintext url, panicing if any problems come up
func get(url string) string {
	response, e := http.Get(url)
	if e != nil {
		panic(e)
	}
	defer response.Body.Close()
	bytes, e := ioutil.ReadAll(response.Body)
	if e != nil {
		panic(e)
	}
	return string(bytes)
}
<commit_msg>Add in AES-128 in ECB mode for Set 1, Challenge 7<commit_after>// http://cryptopals.com/sets/1/
package cryptopals

import (
	"crypto/aes"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"

	"github.com/ianferguson/cryptopals/encodings"
	"github.com/ianferguson/cryptopals/vigenere"
)

// Challenge 1 converts a provided hex string to binary and then from binary to base64, verifying its result
// http://cryptopals.com/sets/1/challenges/1/
func TestSet1Challenge1(test *testing.T) {
	input := "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"
	output := "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"
	bytes := encodings.HexToBytes(input)
	result := encodings.BytesToBase64(bytes)

	if result != output {
		test.Errorf("base64 encoded string was %v but %v was expected", result, input)
	}

}

// Challenge 2 takes tow inputs and xor's them together
// http://cryptopals.com/sets/1/challenges/2/
func TestSet1Challenge2(test *testing.T) {
	input1 := "1c0111001f010100061a024b53535009181c"
	input2 := "686974207468652062756c6c277320657965"
	expected := "746865206b696420646f6e277420706c6179"
	b1 := encodings.HexToBytes(input1)
	b2 := encodings.HexToBytes(input2)
	result := encodings.BytesToHex(xor(b1, b2))
	if result != expected {
		test.Errorf("expected %v but got %v", expected, result)
	}
}

// Challenge 3 is to crack a hex string that has been xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/3/
func TestSet1Challenge3(test *testing.T) {
	hexInput := "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
	output := vigenere.CrackSimple(encodings.HexToBytes(hexInput))
	expected := "Cooking MC's like a pound of bacon"
	if output.Text != expected {
		test.Errorf("expected the input to decode to %v but got %q instead", expected, output.Text)
	}

	if output.Key != 'X' {
		test.Errorf("expected the key to be guessed as 'X' but was %q", output.Key)
	}
}

// Challenge 4 is to locate the 1 string within 60 hex snippets that is a
// an english string xor'd with a 1 byte key
// http://cryptopals.com/sets/1/challenges/4/
func TestSet1Challenge4(test *testing.T) {
	text := get("http://cryptopals.com/static/challenge-data/4.txt")
	expected := "Now that the party is jumping\n"
	// BUG(ian) assumes that 1 and only 1 string in the input text, should probably accumulate all
	// qualifying strings in a slice
	var found string
	for _, code := range strings.Split(text, "\n") {
		s := vigenere.CrackSimple(encodings.HexToBytes(code))
		if s.Score < 0.025 {
			found = s.Text
		}
	}

	if found != expected {
		test.Errorf("expected clear text to be found with message %v but found %q", expected, found)
	}
}

// Challenge 5 is to implement a function that applies a multi byte key to a plaintext cyclically
// http://cryptopals.com/sets/1/challenges/5/
func TestSet1Challenge5(test *testing.T) {
	input := "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"
	expected := "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272" +
		"a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f"
	key := []byte("ICE")
	ciphertext := vigenere.Encrypt([]byte(input), key)
	hextext := encodings.BytesToHex(ciphertext)
	if hextext != expected {
		test.Errorf("Expected %v but got %v", expected, hextext)
	}
}

// Challenge 6 requires detecting the key length of a plaintext xor'd
// with a fixed key, and then cracking that key
// http://cryptopals.com/sets/1/challenges/6/
func TestSet1Challenge6(test *testing.T) {
	ciphertext := get("http://cryptopals.com/static/challenge-data/6.txt")
	expected := whiteboy
	p := string(vigenere.Crack(encodings.Base64ToBytes(ciphertext)))
	if p != expected {
		test.Errorf("expected: %v\nbut got: %q", expected, p)
	}
}

// Challenge 7 is to find a AES ECB encrypted string within a set of strings
func TestSet1Challenge7(test *testing.T) {
	key := []byte("YELLOW SUBMARINE")
	aes, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	ciphertext := encodings.Base64ToBytes(get("http://cryptopals.com/static/challenge-data/7.txt"))
	decrypted := make([]byte, len(ciphertext))
	bs := aes.BlockSize()
	// use the slices like queue's and keep consuming a blocksize
	// at a time, putting them into the next slice in the destination slice
	// note: if you were to get rid of assigning decrypted to d, there would be no handle
	// to the original full slice left to return at the end of the test
	d := decrypted
	for len(ciphertext) > 0 {
		aes.Decrypt(d, ciphertext[:bs])
		//shift the decrypted and source slices start points to the next chunk
		d = d[bs:]
		ciphertext = ciphertext[bs:]
	}

	if !strings.Contains(string(decrypted), "I'm back and I'm ringin' the bell") {
		test.Errorf("invalid paintext returned from AES")
	}
}

// wrapper for getting a plaintext url, panicing if any problems come up
func get(url string) string {
	response, e := http.Get(url)
	if e != nil {
		panic(e)
	}
	defer response.Body.Close()
	bytes, e := ioutil.ReadAll(response.Body)
	if e != nil {
		panic(e)
	}
	return string(bytes)
}

const whiteboy = "I'm back and I'm ringin' the bell \n" +
	"A rockin' on the mike while the fly girls yell \n" +
	"In ecstasy in the back of me \n" +
	"Well that's my DJ Deshay cuttin' all them Z's \n" +
	"Hittin' hard and the girlies goin' crazy \n" +
	"Vanilla's on the mike, man I'm not lazy. \n" +
	"\n" +
	"I'm lettin' my drug kick in \n" +
	"It controls my mouth and I begin \n" +
	"To just let it flow, let my concepts go \n" +
	"My posse's to the side yellin', Go Vanilla Go! \n" +
	"\n" +
	"Smooth 'cause that's the way I will be \n" +
	"And if you don't give a damn, then \n" +
	"Why you starin' at me \n" +
	"So get off 'cause I control the stage \n" +
	"There's no dissin' allowed \n" +
	"I'm in my own phase \n" +
	"The girlies sa y they love me and that is ok \n" +
	"And I can dance better than any kid n' play \n" +
	"\n" +
	"Stage 2 -- Yea the one ya' wanna listen to \n" +
	"It's off my head so let the beat play through \n" +
	"So I can funk it up and make it sound good \n" +
	"1-2-3 Yo -- Knock on some wood \n" +
	"For good luck, I like my rhymes atrocious \n" +
	"Supercalafragilisticexpialidocious \n" +
	"I'm an effect and that you can bet \n" +
	"I can take a fly girl and make her wet. \n" +
	"\n" +
	"I'm like Samson -- Samson to Delilah \n" +
	"There's no denyin', You can try to hang \n" +
	"But you'll keep tryin' to get my style \n" +
	"Over and over, practice makes perfect \n" +
	"But not if you're a loafer. \n" +
	"\n" +
	"You'll get nowhere, no place, no time, no girls \n" +
	"Soon -- Oh my God, homebody, you probably eat \n" +
	"Spaghetti with a spoon! Come on and say it! \n" +
	"\n" +
	"VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino \n" +
	"Intoxicating so you stagger like a wino \n" +
	"So punks stop trying and girl stop cryin' \n" +
	"Vanilla Ice is sellin' and you people are buyin' \n" +
	"'Cause why the freaks are jockin' like Crazy Glue \n" +
	"Movin' and groovin' trying to sing along \n" +
	"All through the ghetto groovin' this here song \n" +
	"Now you're amazed by the VIP posse. \n" +
	"\n" +
	"Steppin' so hard like a German Nazi \n" +
	"Startled by the bases hittin' ground \n" +
	"There's no trippin' on mine, I'm just gettin' down \n" +
	"Sparkamatic, I'm hangin' tight like a fanatic \n" +
	"You trapped me once and I thought that \n" +
	"You might have it \n" +
	"So step down and lend me your ear \n" +
	"'89 in my time! You, '90 is my year. \n" +
	"\n" +
	"You're weakenin' fast, YO! and I can tell it \n" +
	"Your body's gettin' hot, so, so I can smell it \n" +
	"So don't be mad and don't be sad \n" +
	"'Cause the lyrics belong to ICE, You can call me Dad \n" +
	"You're pitchin' a fit, so step back and endure \n" +
	"Let the witch doctor, Ice, do the dance to cure \n" +
	"So come up close and don't be square \n" +
	"You wanna battle me -- Anytime, anywhere \n" +
	"\n" +
	"You thought that I was weak, Boy, you're dead wrong \n" +
	"So come on, everybody and sing this song \n" +
	"\n" +
	"Say -- Play that funky music Say, go white boy, go white boy go \n" +
	"play that funky music Go white boy, go white boy, go \n" +
	"Lay down and boogie and play that funky music till you die. \n" +
	"\n" +
	"Play that funky music Come on, Come on, let me hear \n" +
	"Play that funky music white boy you say it, say it \n" +
	"Play that funky music A little louder now \n" +
	"Play that funky music, white boy Come on, Come on, Come on \n" +
	"Play that funky music \n"
<|endoftext|>