<commit_before>package ethchain

import (
	"bytes"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
	"math"
	"math/big"
)

type BlockChain struct {
	Ethereum EthManager
	// The famous, the fabulous Mister GENESIIIIIIS (block)
	genesisBlock *Block
	// Last known total difficulty
	TD *big.Int

	LastBlockNumber uint64

	CurrentBlock  *Block
	LastBlockHash []byte
}

func NewBlockChain(ethereum EthManager) *BlockChain {
	bc := &BlockChain{}
	bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
	bc.Ethereum = ethereum

	bc.setLastBlock()

	return bc
}

func (bc *BlockChain) Genesis() *Block {
	return bc.genesisBlock
}

func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
	var root interface{}
	var lastBlockTime int64
	hash := ZeroHash256

	if bc.CurrentBlock != nil {
		root = bc.CurrentBlock.state.trie.Root
		hash = bc.LastBlockHash
		lastBlockTime = bc.CurrentBlock.Time
	}
	block := CreateBlock(
		root,
		hash,
		coinbase,
		ethutil.BigPow(2, 32),
		nil,
		"",
		txs)

	if bc.CurrentBlock != nil {
		var mul *big.Int
		if block.Time < lastBlockTime+42 {
			mul = big.NewInt(1)
		} else {
			mul = big.NewInt(-1)
		}

		diff := new(big.Int)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		diff.Div(diff, big.NewInt(1024))
		diff.Mul(diff, mul)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		block.Difficulty = diff
	}

	return block
}

func (bc *BlockChain) HasBlock(hash []byte) bool {
	data, _ := ethutil.Config.Db.Get(hash)
	return len(data) != 0
}

// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
	block := bc.CurrentBlock

	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(hash, block.PrevHash) == 0 {
			return true
		}
	}
	return false
}

func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
	blockDiff := new(big.Int)

	for _, uncle := range block.Uncles {
		blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
	}
	blockDiff = blockDiff.Add(blockDiff, block.Difficulty)

	return blockDiff
}
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
	var blocks []*Block
	for i := 0; i < (msg.Data.Len() - 1); i++ {
		block := NewBlockFromRlpValue(msg.Data.Get(i))
		blocks = append(blocks, block)
	}
	return bc.FindCanonicalChain(blocks, commonBlockHash)
}

// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
// Return true if we are the using the canonical chain false if not
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
	// 1. Calculate TD of the current chain
	// 2. Calculate TD of the new chain
	// Reset state to the correct one

	chainDifficulty := new(big.Int)

	// Calculate the entire chain until the block we both have
	// Start with the newest block we got, all the way back to the common block we both know
	for _, block := range blocks {
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		log.Println("Checking incoming blocks:")
		chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty)

	curChainDifficulty := new(big.Int)
	block := bc.CurrentBlock
	for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
		i++
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		anOtherBlock := bc.GetBlock(block.PrevHash)
		if anOtherBlock == nil {
			// We do not want to count the genesis block for difficulty since that's not being sent
			log.Println("[CHAIN] At genesis block, breaking")
			break
		}
		curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
	if chainDifficulty.Cmp(curChainDifficulty) == 1 {
		log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
		bc.ResetTillBlockHash(commonBlockHash)
		return false
	} else {
		log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.")
		return true
	}
}
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
	lastBlock := bc.CurrentBlock
	var returnTo *Block
	// Reset to Genesis if that's all the origin there is.
	if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
		returnTo = bc.genesisBlock
		bc.CurrentBlock = bc.genesisBlock
		bc.LastBlockHash = bc.genesisBlock.Hash()
		bc.LastBlockNumber = 1
	} else {
		returnTo = bc.GetBlock(hash)
		bc.CurrentBlock = returnTo
		bc.LastBlockHash = returnTo.Hash()
		info := bc.BlockInfo(returnTo)
		bc.LastBlockNumber = info.Number
	}

	bc.Ethereum.StateManager().PrepareDefault(returnTo)

	err := ethutil.Config.Db.Delete(lastBlock.Hash())
	if err != nil {
		return err
	}

	var block *Block
	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(block.Hash(), hash) == 0 {
			log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
			break
		}
		err = ethutil.Config.Db.Delete(block.Hash())
		if err != nil {
			return err
		}
	}
	log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
	return nil
}

func (bc *BlockChain) GenesisBlock() *Block {
	return bc.genesisBlock
}

// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
	var chain []interface{}
	// Get the current hash to start with
	currentHash := bc.CurrentBlock.Hash()
	// Get the last number on the block chain
	lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
	// Get the parents number
	parentNumber := bc.BlockInfoByHash(hash).Number
	// Get the min amount. We might not have max amount of blocks
	count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
	startNumber := parentNumber + count

	num := lastNumber
	for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
		num--
	}
	for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
		// Get the block of the chain
		block := bc.GetBlock(currentHash)
		currentHash = block.PrevHash

		chain = append(chain, block.Value().Val)

		num--
	}

	return chain
}

func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
	genHash := bc.genesisBlock.Hash()

	block := bc.GetBlock(hash)
	var blocks []*Block

	for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
		blocks = append([]*Block{block}, blocks...)

		if bytes.Compare(genHash, block.Hash()) == 0 {
			break
		}
		i++
	}

	return blocks
}

func AddTestNetFunds(block *Block) {
	for _, addr := range []string{
		"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
		"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
		"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
		"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
		"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
	} {
		//log.Println("2^200 Wei to", addr)
		codedAddr := ethutil.FromHex(addr)
		addr := block.state.GetAccount(codedAddr)
		addr.Amount = ethutil.BigPow(2, 200)
		block.state.UpdateAccount(codedAddr, addr)
	}
}

func (bc *BlockChain) setLastBlock() {
	data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
	if len(data) != 0 {
		block := NewBlockFromBytes(data)
		info := bc.BlockInfo(block)
		bc.CurrentBlock = block
		bc.LastBlockHash = block.Hash()
		bc.LastBlockNumber = info.Number

		log.Printf("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
	} else {
		AddTestNetFunds(bc.genesisBlock)

		bc.genesisBlock.state.trie.Sync()
		// Prepare the genesis block
		bc.Add(bc.genesisBlock)

		//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
		//bm.bc.genesisBlock.PrintHash()
	}

	// Set the last know difficulty (might be 0x0 as initial value, Genesis)
	bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())

	log.Printf("Last block: %x\n", bc.CurrentBlock.Hash())
}

func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
	ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
	bc.TD = td
}

// Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) {
	bc.writeBlockInfo(block)
	// Prepare the genesis block

	bc.CurrentBlock = block
	bc.LastBlockHash = block.Hash()

	encodedBlock := block.RlpEncode()
	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}

func (bc *BlockChain) GetBlock(hash []byte) *Block {
	data, _ := ethutil.Config.Db.Get(hash)
	if len(data) == 0 {
		return nil
	}

	return NewBlockFromBytes(data)
}

func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

// Unexported method for writing extra non-essential block info to the db
func (bc *BlockChain) writeBlockInfo(block *Block) {
	bc.LastBlockNumber++
	bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}

	// For now we use the block hash with the words "info" appended as key
	ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}

func (bc *BlockChain) Stop() {
	if bc.CurrentBlock != nil {
		log.Println("[CHAIN] Stopped")
	}
}
<commit_msg>Added todo<commit_after>package ethchain

import (
	"bytes"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
	"math"
	"math/big"
)

type BlockChain struct {
	Ethereum EthManager
	// The famous, the fabulous Mister GENESIIIIIIS (block)
	genesisBlock *Block
	// Last known total difficulty
	TD *big.Int

	LastBlockNumber uint64

	CurrentBlock  *Block
	LastBlockHash []byte
}

func NewBlockChain(ethereum EthManager) *BlockChain {
	bc := &BlockChain{}
	bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
	bc.Ethereum = ethereum

	bc.setLastBlock()

	return bc
}

func (bc *BlockChain) Genesis() *Block {
	return bc.genesisBlock
}

func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block {
	var root interface{}
	var lastBlockTime int64
	hash := ZeroHash256

	if bc.CurrentBlock != nil {
		root = bc.CurrentBlock.state.trie.Root
		hash = bc.LastBlockHash
		lastBlockTime = bc.CurrentBlock.Time
	}
	block := CreateBlock(
		root,
		hash,
		coinbase,
		ethutil.BigPow(2, 32),
		nil,
		"",
		txs)

	if bc.CurrentBlock != nil {
		var mul *big.Int
		if block.Time < lastBlockTime+42 {
			mul = big.NewInt(1)
		} else {
			mul = big.NewInt(-1)
		}

		diff := new(big.Int)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		diff.Div(diff, big.NewInt(1024))
		diff.Mul(diff, mul)
		diff.Add(diff, bc.CurrentBlock.Difficulty)
		block.Difficulty = diff
	}

	return block
}

func (bc *BlockChain) HasBlock(hash []byte) bool {
	data, _ := ethutil.Config.Db.Get(hash)
	return len(data) != 0
}

// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool {
	block := bc.CurrentBlock

	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(hash, block.PrevHash) == 0 {
			return true
		}
	}
	return false
}

func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
	blockDiff := new(big.Int)

	for _, uncle := range block.Uncles {
		blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
	}
	blockDiff = blockDiff.Add(blockDiff, block.Difficulty)

	return blockDiff
}
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
	var blocks []*Block
	for i := 0; i < (msg.Data.Len() - 1); i++ {
		block := NewBlockFromRlpValue(msg.Data.Get(i))
		blocks = append(blocks, block)
	}
	return bc.FindCanonicalChain(blocks, commonBlockHash)
}

// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
// Return true if we are the using the canonical chain false if not
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
	// 1. Calculate TD of the current chain
	// 2. Calculate TD of the new chain
	// Reset state to the correct one

	chainDifficulty := new(big.Int)

	// Calculate the entire chain until the block we both have
	// Start with the newest block we got, all the way back to the common block we both know
	for _, block := range blocks {
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		log.Println("Checking incoming blocks:")
		chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty)

	curChainDifficulty := new(big.Int)
	block := bc.CurrentBlock
	for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
		i++
		if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
			log.Println("[CHAIN] We have found the common parent block, breaking")
			break
		}
		anOtherBlock := bc.GetBlock(block.PrevHash)
		if anOtherBlock == nil {
			// We do not want to count the genesis block for difficulty since that's not being sent
			log.Println("[CHAIN] At genesis block, breaking")
			break
		}
		curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
	}

	log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty)
	if chainDifficulty.Cmp(curChainDifficulty) == 1 {
		log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash)
		bc.ResetTillBlockHash(commonBlockHash)
		return false
	} else {
		log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.")
		return true
	}
}
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
	lastBlock := bc.CurrentBlock
	var returnTo *Block
	// Reset to Genesis if that's all the origin there is.
	if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
		returnTo = bc.genesisBlock
		bc.CurrentBlock = bc.genesisBlock
		bc.LastBlockHash = bc.genesisBlock.Hash()
		bc.LastBlockNumber = 1
	} else {
		// TODO: Somehow this doesn't really give the right numbers, double check.
		// TODO: Change logs into debug lines
		returnTo = bc.GetBlock(hash)
		bc.CurrentBlock = returnTo
		bc.LastBlockHash = returnTo.Hash()
		info := bc.BlockInfo(returnTo)
		bc.LastBlockNumber = info.Number
	}

	bc.Ethereum.StateManager().PrepareDefault(returnTo)

	err := ethutil.Config.Db.Delete(lastBlock.Hash())
	if err != nil {
		return err
	}

	var block *Block
	for ; block != nil; block = bc.GetBlock(block.PrevHash) {
		if bytes.Compare(block.Hash(), hash) == 0 {
			log.Println("[CHAIN] We have arrived at the the common parent block, breaking")
			break
		}
		err = ethutil.Config.Db.Delete(block.Hash())
		if err != nil {
			return err
		}
	}
	log.Println("[CHAIN] Split chain deleted and reverted to common parent block.")
	return nil
}

func (bc *BlockChain) GenesisBlock() *Block {
	return bc.genesisBlock
}

// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
	var chain []interface{}
	// Get the current hash to start with
	currentHash := bc.CurrentBlock.Hash()
	// Get the last number on the block chain
	lastNumber := bc.BlockInfo(bc.CurrentBlock).Number
	// Get the parents number
	parentNumber := bc.BlockInfoByHash(hash).Number
	// Get the min amount. We might not have max amount of blocks
	count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
	startNumber := parentNumber + count

	num := lastNumber
	for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash {
		num--
	}
	for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
		// Get the block of the chain
		block := bc.GetBlock(currentHash)
		currentHash = block.PrevHash

		chain = append(chain, block.Value().Val)

		num--
	}

	return chain
}

func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
	genHash := bc.genesisBlock.Hash()

	block := bc.GetBlock(hash)
	var blocks []*Block

	for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
		blocks = append([]*Block{block}, blocks...)

		if bytes.Compare(genHash, block.Hash()) == 0 {
			break
		}
		i++
	}

	return blocks
}

func AddTestNetFunds(block *Block) {
	for _, addr := range []string{
		"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
		"e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
		"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
		"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
		"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran
	} {
		//log.Println("2^200 Wei to", addr)
		codedAddr := ethutil.FromHex(addr)
		addr := block.state.GetAccount(codedAddr)
		addr.Amount = ethutil.BigPow(2, 200)
		block.state.UpdateAccount(codedAddr, addr)
	}
}

func (bc *BlockChain) setLastBlock() {
	data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
	if len(data) != 0 {
		block := NewBlockFromBytes(data)
		info := bc.BlockInfo(block)
		bc.CurrentBlock = block
		bc.LastBlockHash = block.Hash()
		bc.LastBlockNumber = info.Number

		log.Printf("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber)
	} else {
		AddTestNetFunds(bc.genesisBlock)

		bc.genesisBlock.state.trie.Sync()
		// Prepare the genesis block
		bc.Add(bc.genesisBlock)

		//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
		//bm.bc.genesisBlock.PrintHash()
	}

	// Set the last know difficulty (might be 0x0 as initial value, Genesis)
	bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())

	log.Printf("Last block: %x\n", bc.CurrentBlock.Hash())
}

func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
	ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes())
	bc.TD = td
}

// Add a block to the chain and record addition information
func (bc *BlockChain) Add(block *Block) {
	bc.writeBlockInfo(block)
	// Prepare the genesis block

	bc.CurrentBlock = block
	bc.LastBlockHash = block.Hash()

	encodedBlock := block.RlpEncode()
	ethutil.Config.Db.Put(block.Hash(), encodedBlock)
	ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}

func (bc *BlockChain) GetBlock(hash []byte) *Block {
	data, _ := ethutil.Config.Db.Get(hash)
	if len(data) == 0 {
		return nil
	}

	return NewBlockFromBytes(data)
}

func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

func (bc *BlockChain) BlockInfo(block *Block) BlockInfo {
	bi := BlockInfo{}
	data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
	bi.RlpDecode(data)

	return bi
}

// Unexported method for writing extra non-essential block info to the db
func (bc *BlockChain) writeBlockInfo(block *Block) {
	bc.LastBlockNumber++
	bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash}

	// For now we use the block hash with the words "info" appended as key
	ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
}

func (bc *BlockChain) Stop() {
	if bc.CurrentBlock != nil {
		log.Println("[CHAIN] Stopped")
	}
}
<|endoftext|>


<commit_before>package eval

//go:generate ./builtin_modules.bash

// Builtin special forms.

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/elves/elvish/parse"
)

type compileBuiltin func(*compiler, *parse.Form) OpFunc

var ErrNoDataDir = errors.New("There is no data directory")

var builtinSpecials map[string]compileBuiltin

// BuiltinSpecialNames contains all names of builtin special forms. It is
// useful for the syntax highlighter.
var BuiltinSpecialNames []string

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]compileBuiltin{
		"del": compileDel,
		"fn":  compileFn,
		"use": compileUse,
	}
	for k := range builtinSpecials {
		BuiltinSpecialNames = append(BuiltinSpecialNames, k)
	}
}

// DelForm = 'del' { VariablePrimary }
func compileDel(cp *compiler, fn *parse.Form) OpFunc {
	// Do conventional compiling of all compound expressions, including
	// ensuring that variables can be resolved
	var names, envNames []string
	for _, cn := range fn.Args {
		cp.compiling(cn)
		qname := mustString(cp, cn, "should be a literal variable name")
		explode, ns, name := ParseAndFixVariable(qname)
		if explode {
			cp.errorf("removing exploded variable makes no sense")
		}
		switch ns {
		case "", "local":
			if !cp.thisScope()[name] {
				cp.errorf("variable $%s not found on current local scope", name)
			}
			delete(cp.thisScope(), name)
			names = append(names, name)
		case "E":
			envNames = append(envNames, name)
		default:
			cp.errorf("can only delete a variable in local: or E:")
		}

	}
	return func(ec *EvalCtx) {
		for _, name := range names {
			delete(ec.local, name)
		}
		for _, name := range envNames {
			// BUG(xiaq): We rely on the fact that os.Unsetenv always returns
			// nil.
			os.Unsetenv(name)
		}
	}
}

// makeFnOp wraps an op such that a return is converted to an ok.
func makeFnOp(op Op) Op {
	return Op{func(ec *EvalCtx) {
		err := ec.PEval(op)
		if err != nil && err.(*Exception).Cause != Return {
			// rethrow
			throw(err)
		}
	}, op.Begin, op.End}
}

// FnForm = 'fn' StringPrimary LambdaPrimary
//
// fn f []{foobar} is a shorthand for set '&'f = []{foobar}.
func compileFn(cp *compiler, fn *parse.Form) OpFunc {
	if len(fn.Args) == 0 {
		end := fn.End()
		cp.errorpf(end, end, "should be followed by function name")
	}
	fnName := mustString(cp, fn.Args[0], "must be a literal string")
	varName := FnPrefix + fnName

	if len(fn.Args) == 1 {
		end := fn.Args[0].End()
		cp.errorpf(end, end, "should be followed by a lambda")
	}
	pn := mustPrimary(cp, fn.Args[1], "should be a lambda")
	if pn.Type != parse.Lambda {
		cp.compiling(pn)
		cp.errorf("should be a lambda")
	}
	if len(fn.Args) > 2 {
		cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
	}

	cp.registerVariableSet(":" + varName)
	op := cp.lambda(pn)

	return func(ec *EvalCtx) {
		// Initialize the function variable with the builtin nop
		// function. This step allows the definition of recursive
		// functions; the actual function will never be called.
		ec.local[varName] = NewPtrVariable(&BuiltinFn{"<shouldn't be called>", nop})
		closure := op(ec)[0].(*Closure)
		closure.Op = makeFnOp(closure.Op)
		ec.local[varName].Set(closure)
	}
}

// UseForm = 'use' StringPrimary [ Compound ]
func compileUse(cp *compiler, fn *parse.Form) OpFunc {
	var modname string
	var filenameOp ValuesOp
	var filenameBegin, filenameEnd int

	switch len(fn.Args) {
	case 0:
		end := fn.Head.End()
		cp.errorpf(end, end, "lack module name")
	case 2:
		filenameOp = cp.compoundOp(fn.Args[1])
		filenameBegin = fn.Args[1].Begin()
		filenameEnd = fn.Args[1].End()
		fallthrough
	case 1:
		modname = mustString(cp, fn.Args[0], "should be a literal module name")
	default:
		cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
	}

	return func(ec *EvalCtx) {
		if filenameOp.Func != nil {
			values := filenameOp.Exec(ec)
			valuesMust := &muster{ec, "module filename", filenameBegin, filenameEnd, values}
			filename := string(valuesMust.mustOneStr())
			use(ec, modname, &filename)
		} else {
			use(ec, modname, nil)
		}
	}
}

func use(ec *EvalCtx, modname string, pfilename *string) {
	if _, ok := ec.Evaler.Modules[modname]; ok {
		// Module already loaded.
		return
	}

	// Load the source.
	var filename, source string

	if pfilename != nil {
		filename = *pfilename
		var err error
		source, err = readFileUTF8(filename)
		maybeThrow(err)
	} else {
		// No filename; defaulting to $datadir/$modname.elv.
		if ec.DataDir == "" {
			throw(ErrNoDataDir)
		}
		filename = ec.DataDir + "/" + strings.Replace(modname, ":", "/", -1) + ".elv"
		if _, err := os.Stat(filename); os.IsNotExist(err) {
			// File does not exist. Try loading from the table of builtin
			// modules.
			var ok bool
			if source, ok = builtinModules[modname]; ok {
				// Source is loaded. Do nothing more.
				filename = "<builtin module>"
			} else {
				throw(fmt.Errorf("cannot load %s: %s does not exist", modname, filename))
			}
		} else {
			// File exists. Load it.
			source, err = readFileUTF8(filename)
			maybeThrow(err)
		}
	}

	n, err := parse.Parse(filename, source)
	maybeThrow(err)

	// Make an empty namespace.
	local := Namespace{}

	// TODO(xiaq): Should handle failures when evaluting the module
	newEc := &EvalCtx{
		ec.Evaler, "module " + modname,
		filename, source,
		local, Namespace{},
		ec.ports, nil, true,
		0, len(source), ec.addTraceback(), false,
	}

	op, err := newEc.Compile(n, filename, source)
	// TODO the err originates in another source, should add appropriate information.
	maybeThrow(err)

	// Load the namespace before executing. This avoids mutual and self use's to
	// result in an infinite recursion.
	ec.Evaler.Modules[modname] = local
	err = newEc.PEval(op)
	if err != nil {
		// Unload the namespace.
		delete(ec.Modules, modname)
		throw(err)
	}
}
<commit_msg>Find libraries in ~/.elvish/lib when use'ing.<commit_after>package eval

//go:generate ./builtin_modules.bash

// Builtin special forms.

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/elves/elvish/parse"
)

type compileBuiltin func(*compiler, *parse.Form) OpFunc

var ErrNoDataDir = errors.New("There is no data directory")

var builtinSpecials map[string]compileBuiltin

// BuiltinSpecialNames contains all names of builtin special forms. It is
// useful for the syntax highlighter.
var BuiltinSpecialNames []string

func init() {
	// Needed to avoid initialization loop
	builtinSpecials = map[string]compileBuiltin{
		"del": compileDel,
		"fn":  compileFn,
		"use": compileUse,
	}
	for k := range builtinSpecials {
		BuiltinSpecialNames = append(BuiltinSpecialNames, k)
	}
}

// DelForm = 'del' { VariablePrimary }
func compileDel(cp *compiler, fn *parse.Form) OpFunc {
	// Do conventional compiling of all compound expressions, including
	// ensuring that variables can be resolved
	var names, envNames []string
	for _, cn := range fn.Args {
		cp.compiling(cn)
		qname := mustString(cp, cn, "should be a literal variable name")
		explode, ns, name := ParseAndFixVariable(qname)
		if explode {
			cp.errorf("removing exploded variable makes no sense")
		}
		switch ns {
		case "", "local":
			if !cp.thisScope()[name] {
				cp.errorf("variable $%s not found on current local scope", name)
			}
			delete(cp.thisScope(), name)
			names = append(names, name)
		case "E":
			envNames = append(envNames, name)
		default:
			cp.errorf("can only delete a variable in local: or E:")
		}

	}
	return func(ec *EvalCtx) {
		for _, name := range names {
			delete(ec.local, name)
		}
		for _, name := range envNames {
			// BUG(xiaq): We rely on the fact that os.Unsetenv always returns
			// nil.
			os.Unsetenv(name)
		}
	}
}

// makeFnOp wraps an op such that a return is converted to an ok.
func makeFnOp(op Op) Op {
	return Op{func(ec *EvalCtx) {
		err := ec.PEval(op)
		if err != nil && err.(*Exception).Cause != Return {
			// rethrow
			throw(err)
		}
	}, op.Begin, op.End}
}

// FnForm = 'fn' StringPrimary LambdaPrimary
//
// fn f []{foobar} is a shorthand for set '&'f = []{foobar}.
func compileFn(cp *compiler, fn *parse.Form) OpFunc {
	if len(fn.Args) == 0 {
		end := fn.End()
		cp.errorpf(end, end, "should be followed by function name")
	}
	fnName := mustString(cp, fn.Args[0], "must be a literal string")
	varName := FnPrefix + fnName

	if len(fn.Args) == 1 {
		end := fn.Args[0].End()
		cp.errorpf(end, end, "should be followed by a lambda")
	}
	pn := mustPrimary(cp, fn.Args[1], "should be a lambda")
	if pn.Type != parse.Lambda {
		cp.compiling(pn)
		cp.errorf("should be a lambda")
	}
	if len(fn.Args) > 2 {
		cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
	}

	cp.registerVariableSet(":" + varName)
	op := cp.lambda(pn)

	return func(ec *EvalCtx) {
		// Initialize the function variable with the builtin nop
		// function. This step allows the definition of recursive
		// functions; the actual function will never be called.
		ec.local[varName] = NewPtrVariable(&BuiltinFn{"<shouldn't be called>", nop})
		closure := op(ec)[0].(*Closure)
		closure.Op = makeFnOp(closure.Op)
		ec.local[varName].Set(closure)
	}
}

// UseForm = 'use' StringPrimary [ Compound ]
func compileUse(cp *compiler, fn *parse.Form) OpFunc {
	var modname string
	var filenameOp ValuesOp
	var filenameBegin, filenameEnd int

	switch len(fn.Args) {
	case 0:
		end := fn.Head.End()
		cp.errorpf(end, end, "lack module name")
	case 2:
		filenameOp = cp.compoundOp(fn.Args[1])
		filenameBegin = fn.Args[1].Begin()
		filenameEnd = fn.Args[1].End()
		fallthrough
	case 1:
		modname = mustString(cp, fn.Args[0], "should be a literal module name")
	default:
		cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
	}

	return func(ec *EvalCtx) {
		if filenameOp.Func != nil {
			values := filenameOp.Exec(ec)
			valuesMust := &muster{ec, "module filename", filenameBegin, filenameEnd, values}
			filename := string(valuesMust.mustOneStr())
			use(ec, modname, &filename)
		} else {
			use(ec, modname, nil)
		}
	}
}

func use(ec *EvalCtx, modname string, pfilename *string) {
	if _, ok := ec.Evaler.Modules[modname]; ok {
		// Module already loaded.
		return
	}

	// Load the source.
	var filename, source string

	if pfilename != nil {
		filename = *pfilename
		var err error
		source, err = readFileUTF8(filename)
		maybeThrow(err)
	} else {
		// No filename; defaulting to $datadir/lib/$modname.elv.
		if ec.DataDir == "" {
			throw(ErrNoDataDir)
		}
		filename = ec.DataDir + "/lib/" + strings.Replace(modname, ":", "/", -1) + ".elv"
		if _, err := os.Stat(filename); os.IsNotExist(err) {
			// File does not exist. Try loading from the table of builtin
			// modules.
			var ok bool
			if source, ok = builtinModules[modname]; ok {
				// Source is loaded. Do nothing more.
				filename = "<builtin module>"
			} else {
				throw(fmt.Errorf("cannot load %s: %s does not exist", modname, filename))
			}
		} else {
			// File exists. Load it.
			source, err = readFileUTF8(filename)
			maybeThrow(err)
		}
	}

	n, err := parse.Parse(filename, source)
	maybeThrow(err)

	// Make an empty namespace.
	local := Namespace{}

	// TODO(xiaq): Should handle failures when evaluting the module
	newEc := &EvalCtx{
		ec.Evaler, "module " + modname,
		filename, source,
		local, Namespace{},
		ec.ports, nil, true,
		0, len(source), ec.addTraceback(), false,
	}

	op, err := newEc.Compile(n, filename, source)
	// TODO the err originates in another source, should add appropriate information.
	maybeThrow(err)

	// Load the namespace before executing. This avoids mutual and self use's to
	// result in an infinite recursion.
	ec.Evaler.Modules[modname] = local
	err = newEc.PEval(op)
	if err != nil {
		// Unload the namespace.
		delete(ec.Modules, modname)
		throw(err)
	}
}
<|endoftext|>


<commit_before>package bundled

const epmElv = `# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (edit:styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (edit:styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (edit:styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (edit:styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  put ?(test -e (dest $pkg))
}

fn -package-domain [pkg]{
  splits &max=2 / $pkg | take 1
}

fn -package-without-domain [pkg]{
  splits &max=2 / $pkg | drop 1 | joins ''
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# received two arguments: package name and the domain config entry
#
# - Method 'git' requires the key 'protocol' in the domain config,
#   which has to be 'http' or 'https'
# - Method 'rsync' requires the key 'location' in the domain config,
#   which has to contain the directory where the domain files are
#   stored. It can be any source location understood by the rsync
#   command.
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      git -C $dest pull
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[location]/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

fn -write-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  mkdir -p (dirname $cfgfile)
  if (has-key $-default-domain-config $dom) {
    put $-default-domain-config[$dom] | to-json > $cfgfile
  } else {
    -error "No default config exists for domain "$dom"."
  }
}

# Returns the domain config file for a given domain. If the file does not
# exist but we have a built-in definition, then we return the
# default. Otherwise we return $false, so the result can always be
# checked with 'if'.
fn -domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  cfg = $false
  if ?(test -f $cfgfile) {
    # If the config file exists, read it...
    cfg = (cat $cfgfile | from-json)
    -debug "Read domain config for "$dom": "(to-string $cfg)
  } else {
    # ...otherwise check if we have a default config for the domain, and save it
    if (has-key $-default-domain-config $dom) {
      cfg = $-default-domain-config[$dom]
      -debug "No existing config for "$dom", using the default: "(to-string $cfg)
    } else {
      -debug "No existing config for "$dom" and no default available."
    }
  }
  put $cfg
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "Unknown operation '"$what"' for package "$pkg
      }
    } else {
      fail "Unknown method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  } else {
    -error "No config for domain '"$dom"'."
  }
}

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  res = [&]
  mdata = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $mdata)) {
    res = (cat $mdata | from-json)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  echo (edit:styled "Package "$pkg cyan)
  echo (edit:styled "Source:" blue) (-package-method $pkg) (-package-op $pkg src)
  if (is-installed $pkg) {
    echo (edit:styled "Installed at "(dest $pkg) green)
  } else {
    echo (edit:styled "Not installed" red)
  }
  keys $data | each [key]{
    echo (edit:styled $key":" blue) $data[$key]
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# List installed packages
fn installed {
  e:ls $-lib-dir | each [dom]{
    cfg = (-domain-config $dom)
    # Only list domains for which we know the config, so that the user
    # can have his own non-package directories under ~/.elvish/lib
    # without conflicts.
    if $cfg {
      lvl = $cfg[levels]
      find $-lib-dir/$dom -type d -depth $lvl | each [pkg]{
        replaces $-lib-dir/ "" $pkg
      }
    }
  }
}

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    -error 'Must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<commit_msg>Force output of is-installed to $true or $false<commit_after>package bundled

const epmElv = `# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (edit:styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (edit:styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (edit:styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (edit:styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  put (or (and ?(test -e (dest $pkg)) $true) $false)
}

fn -package-domain [pkg]{
  splits &max=2 / $pkg | take 1
}

fn -package-without-domain [pkg]{
  splits &max=2 / $pkg | drop 1 | joins ''
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# received two arguments: package name and the domain config entry
#
# - Method 'git' requires the key 'protocol' in the domain config,
#   which has to be 'http' or 'https'
# - Method 'rsync' requires the key 'location' in the domain config,
#   which has to contain the directory where the domain files are
#   stored. It can be any source location understood by the rsync
#   command.
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      git -C $dest pull
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[location]/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

fn -write-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  mkdir -p (dirname $cfgfile)
  if (has-key $-default-domain-config $dom) {
    put $-default-domain-config[$dom] | to-json > $cfgfile
  } else {
    -error "No default config exists for domain "$dom"."
  }
}

# Returns the domain config file for a given domain. If the file does not
# exist but we have a built-in definition, then we return the
# default. Otherwise we return $false, so the result can always be
# checked with 'if'.
fn -domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  cfg = $false
  if ?(test -f $cfgfile) {
    # If the config file exists, read it...
    cfg = (cat $cfgfile | from-json)
    -debug "Read domain config for "$dom": "(to-string $cfg)
  } else {
    # ...otherwise check if we have a default config for the domain, and save it
    if (has-key $-default-domain-config $dom) {
      cfg = $-default-domain-config[$dom]
      -debug "No existing config for "$dom", using the default: "(to-string $cfg)
    } else {
      -debug "No existing config for "$dom" and no default available."
    }
  }
  put $cfg
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "Unknown operation '"$what"' for package "$pkg
      }
    } else {
      fail "Unknown method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  } else {
    -error "No config for domain '"$dom"'."
  }
}

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  res = [&]
  mdata = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $mdata)) {
    res = (cat $mdata | from-json)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  echo (edit:styled "Package "$pkg cyan)
  echo (edit:styled "Source:" blue) (-package-method $pkg) (-package-op $pkg src)
  if (is-installed $pkg) {
    echo (edit:styled "Installed at "(dest $pkg) green)
  } else {
    echo (edit:styled "Not installed" red)
  }
  keys $data | each [key]{
    echo (edit:styled $key":" blue) $data[$key]
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# List installed packages
fn installed {
  e:ls $-lib-dir | each [dom]{
    cfg = (-domain-config $dom)
    # Only list domains for which we know the config, so that the user
    # can have his own non-package directories under ~/.elvish/lib
    # without conflicts.
    if $cfg {
      lvl = $cfg[levels]
      find $-lib-dir/$dom -type d -depth $lvl | each [pkg]{
        replaces $-lib-dir/ "" $pkg
      }
    }
  }
}

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    -error 'Must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<|endoftext|>


<commit_before>package auth

import (
	"crypto/tls"
	"encoding/json"
	"net/http"
	"net/url"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/backend"
	"github.com/gravitational/teleport/lib/httplib"
	"github.com/gravitational/teleport/lib/services"

	"github.com/gravitational/roundtrip"
	"github.com/gravitational/trace"

	log "github.com/Sirupsen/logrus"
)

func (a *AuthServer) getTrustedCluster(name string) (services.TrustedCluster, error) {
	return a.GetTrustedCluster(name)
}

func (a *AuthServer) getTrustedClusters() ([]services.TrustedCluster, error) {
	return a.GetTrustedClusters()
}

func (a *AuthServer) upsertTrustedCluster(trustedCluster services.TrustedCluster) error {
	if trustedCluster.GetEnabled() {
		err := a.enableTrustedCluster(trustedCluster)
		if err != nil {
			return trace.Wrap(err)
		}
	} else {
		err := a.disableTrustedCluster(trustedCluster)
		if err != nil {
			return trace.Wrap(err)
		}
	}

	err := a.UpsertTrustedCluster(trustedCluster)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

func (a *AuthServer) enableTrustedCluster(trustedCluster services.TrustedCluster) error {
	// get the certificate authorities for this auth server
	localCertAuthorities, err := a.GetCertAuthorities(services.HostCA, false)
	if err != nil {
		return trace.Wrap(err)
	}

	// create a reques to validate a trusted cluster (token and local certificate authorities)
	validateRequest := ValidateTrustedClusterRequest{
		Token: trustedCluster.GetToken(),
		CAs:   localCertAuthorities,
	}

	// send the request to the remote auth server via the proxy
	validateResponse, err := a.sendValidateRequestToProxy(trustedCluster.GetProxyAddress(), &validateRequest)
	if err != nil {
		return trace.Wrap(err)
	}

	// the remote auth server has verified our token. add the
	// remote certificate authority to our backend
	for _, remoteCertAuthority := range validateResponse.CAs {

		// add roles into user certificates
		if remoteCertAuthority.GetType() == services.UserCA {
			for _, r := range trustedCluster.GetRoles() {
				remoteCertAuthority.AddRole(r)
			}
		}

		err = a.UpsertCertAuthority(remoteCertAuthority, backend.Forever)
		if err != nil {
			return trace.Wrap(err)
		}
	}

	// the remote auth server has verified our token. add the
	// reverse tunnel into our backend
	reverseTunnel := services.NewReverseTunnel(
		trustedCluster.GetName(),
		[]string{trustedCluster.GetReverseTunnelAddress()},
	)
	err = a.UpsertReverseTunnel(reverseTunnel, backend.Forever)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

func (a *AuthServer) disableTrustedCluster(trustedCluster services.TrustedCluster) error {
	err := a.DeleteCertAuthority(services.CertAuthID{Type: services.UserCA, DomainName: trustedCluster.GetName()})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteCertAuthority(services.CertAuthID{Type: services.HostCA, DomainName: trustedCluster.GetName()})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteReverseTunnel(trustedCluster.GetName())
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	return nil
}

func (a *AuthServer) validateTrustedCluster(validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
	// validate that we generated the token
	err := a.validateTrustedClusterToken(validateRequest.Token)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// token has been validated, upsert the given certificate authority
	for _, certAuthority := range validateRequest.CAs {
		err = a.UpsertCertAuthority(certAuthority, backend.Forever)
		if err != nil {
			return nil, trace.Wrap(err)
		}
	}

	// export our certificate authority and return it to the cluster
	validateResponse := ValidateTrustedClusterResponse{
		CAs: []services.CertAuthority{},
	}
	for _, caType := range []services.CertAuthType{services.HostCA, services.UserCA} {
		certAuthorities, err := a.GetCertAuthorities(caType, false)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		for _, certAuthority := range certAuthorities {
			validateResponse.CAs = append(validateResponse.CAs, certAuthority)
		}
	}

	return &validateResponse, nil
}

func (a *AuthServer) validateTrustedClusterToken(token string) error {
	roles, err := a.ValidateToken(token)
	if err != nil {
		return trace.AccessDenied("invalid token")
	}

	if !roles.Include(teleport.RoleTrustedCluster) {
		return trace.AccessDenied("role does not match")
	}

	if !a.checkTokenTTL(token) {
		return trace.AccessDenied("expired token")
	}

	return nil
}

func (a *AuthServer) deleteTrustedCluster(name string) error {
	err := a.DeleteCertAuthority(services.CertAuthID{Type: services.HostCA, DomainName: name})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteCertAuthority(services.CertAuthID{Type: services.UserCA, DomainName: name})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteReverseTunnel(name)
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteTrustedCluster(name)
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	return nil
}

func (s *AuthServer) sendValidateRequestToProxy(host string, validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
	proxyAddr := url.URL{
		Scheme: "https",
		Host:   host,
	}

	var opts []roundtrip.ClientParam
	if s.DeveloperMode {
		log.Warn("InsecureSkipVerify used to communicate with proxy.")
		log.Warn("Make sure you intend to run Teleport in debug mode.")

		insecureWebClient := &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}
		opts = append(opts, roundtrip.HTTPClient(insecureWebClient))
	}

	clt, err := roundtrip.NewClient(proxyAddr.String(), teleport.WebAPIVersion, opts...)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	validateRequestRaw, err := validateRequest.ToRaw()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	out, err := httplib.ConvertResponse(clt.PostJSON(clt.Endpoint("webapi", "trustedclusters", "validate"), validateRequestRaw))
	if err != nil {
		return nil, trace.Wrap(err)
	}

	var validateResponseRaw *ValidateTrustedClusterResponseRaw
	err = json.Unmarshal(out.Bytes(), &validateResponseRaw)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	validateResponse, err := validateResponseRaw.ToNative()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return validateResponse, nil
}

type ValidateTrustedClusterRequest struct {
	Token string                   `json:"token"`
	CAs   []services.CertAuthority `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterRequest) ToRaw() (*ValidateTrustedClusterRequestRaw, error) {
	cas := [][]byte{}

	for _, certAuthority := range v.CAs {
		data, err := services.GetCertAuthorityMarshaler().MarshalCertAuthority(certAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, data)
	}

	return &ValidateTrustedClusterRequestRaw{
		Token: v.Token,
		CAs:   cas,
	}, nil
}

type ValidateTrustedClusterRequestRaw struct {
	Token string   `json:"token"`
	CAs   [][]byte `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterRequestRaw) ToNative() (*ValidateTrustedClusterRequest, error) {
	cas := []services.CertAuthority{}

	for _, rawCertAuthority := range v.CAs {
		certAuthority, err := services.GetCertAuthorityMarshaler().UnmarshalCertAuthority(rawCertAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, certAuthority)
	}

	return &ValidateTrustedClusterRequest{
		Token: v.Token,
		CAs:   cas,
	}, nil
}

type ValidateTrustedClusterResponse struct {
	CAs []services.CertAuthority `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterResponse) ToRaw() (*ValidateTrustedClusterResponseRaw, error) {
	cas := [][]byte{}

	for _, certAuthority := range v.CAs {
		data, err := services.GetCertAuthorityMarshaler().MarshalCertAuthority(certAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, data)
	}

	return &ValidateTrustedClusterResponseRaw{
		CAs: cas,
	}, nil
}

type ValidateTrustedClusterResponseRaw struct {
	CAs [][]byte `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterResponseRaw) ToNative() (*ValidateTrustedClusterResponse, error) {
	cas := []services.CertAuthority{}

	for _, rawCertAuthority := range v.CAs {
		certAuthority, err := services.GetCertAuthorityMarshaler().UnmarshalCertAuthority(rawCertAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, certAuthority)
	}

	return &ValidateTrustedClusterResponse{
		CAs: cas,
	}, nil
}
<commit_msg>fix trusted cluster key exchange<commit_after>package auth

import (
	"crypto/tls"
	"encoding/json"
	"net/http"
	"net/url"

	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/backend"
	"github.com/gravitational/teleport/lib/httplib"
	"github.com/gravitational/teleport/lib/services"

	"github.com/gravitational/roundtrip"
	"github.com/gravitational/trace"

	log "github.com/Sirupsen/logrus"
)

func (a *AuthServer) getTrustedCluster(name string) (services.TrustedCluster, error) {
	return a.GetTrustedCluster(name)
}

func (a *AuthServer) getTrustedClusters() ([]services.TrustedCluster, error) {
	return a.GetTrustedClusters()
}

func (a *AuthServer) upsertTrustedCluster(trustedCluster services.TrustedCluster) error {
	if trustedCluster.GetEnabled() {
		err := a.enableTrustedCluster(trustedCluster)
		if err != nil {
			return trace.Wrap(err)
		}
	} else {
		err := a.disableTrustedCluster(trustedCluster)
		if err != nil {
			return trace.Wrap(err)
		}
	}

	err := a.UpsertTrustedCluster(trustedCluster)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

func (a *AuthServer) enableTrustedCluster(trustedCluster services.TrustedCluster) error {
	// get the certificate authorities for this auth server
	localCertAuthorities, err := a.GetCertAuthorities(services.HostCA, false)
	if err != nil {
		return trace.Wrap(err)
	}

	// create a request to validate a trusted cluster (token and local certificate authorities)
	validateRequest := ValidateTrustedClusterRequest{
		Token: trustedCluster.GetToken(),
		CAs:   localCertAuthorities,
	}

	// send the request to the remote auth server via the proxy
	validateResponse, err := a.sendValidateRequestToProxy(trustedCluster.GetProxyAddress(), &validateRequest)
	if err != nil {
		return trace.Wrap(err)
	}

	// the remote auth server has verified our token. add the
	// remote certificate authority to our backend
	for _, remoteCertAuthority := range validateResponse.CAs {

		// add roles into user certificates
		if remoteCertAuthority.GetType() == services.UserCA {
			for _, r := range trustedCluster.GetRoles() {
				remoteCertAuthority.AddRole(r)
			}
		}

		err = a.UpsertCertAuthority(remoteCertAuthority, backend.Forever)
		if err != nil {
			return trace.Wrap(err)
		}
	}

	// the remote auth server has verified our token. add the
	// reverse tunnel into our backend
	reverseTunnel := services.NewReverseTunnel(
		trustedCluster.GetName(),
		[]string{trustedCluster.GetReverseTunnelAddress()},
	)
	err = a.UpsertReverseTunnel(reverseTunnel, backend.Forever)
	if err != nil {
		return trace.Wrap(err)
	}

	return nil
}

func (a *AuthServer) disableTrustedCluster(trustedCluster services.TrustedCluster) error {
	err := a.DeleteCertAuthority(services.CertAuthID{Type: services.UserCA, DomainName: trustedCluster.GetName()})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteCertAuthority(services.CertAuthID{Type: services.HostCA, DomainName: trustedCluster.GetName()})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteReverseTunnel(trustedCluster.GetName())
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	return nil
}

func (a *AuthServer) validateTrustedCluster(validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
	// validate that we generated the token
	err := a.validateTrustedClusterToken(validateRequest.Token)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	// token has been validated, upsert the given certificate authority
	for _, certAuthority := range validateRequest.CAs {
		err = a.UpsertCertAuthority(certAuthority, backend.Forever)
		if err != nil {
			return nil, trace.Wrap(err)
		}
	}

	// export our certificate authority and return it to the cluster
	validateResponse := ValidateTrustedClusterResponse{
		CAs: []services.CertAuthority{},
	}
	for _, caType := range []services.CertAuthType{services.HostCA, services.UserCA} {
		certAuthorities, err := a.GetCertAuthorities(caType, false)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		for _, certAuthority := range certAuthorities {
			if certAuthority.GetClusterName() == a.DomainName {
				validateResponse.CAs = append(validateResponse.CAs, certAuthority)
			}
		}
	}

	return &validateResponse, nil
}

func (a *AuthServer) validateTrustedClusterToken(token string) error {
	roles, err := a.ValidateToken(token)
	if err != nil {
		return trace.AccessDenied("invalid token")
	}

	if !roles.Include(teleport.RoleTrustedCluster) {
		return trace.AccessDenied("role does not match")
	}

	if !a.checkTokenTTL(token) {
		return trace.AccessDenied("expired token")
	}

	return nil
}

func (a *AuthServer) deleteTrustedCluster(name string) error {
	err := a.DeleteCertAuthority(services.CertAuthID{Type: services.HostCA, DomainName: name})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteCertAuthority(services.CertAuthID{Type: services.UserCA, DomainName: name})
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteReverseTunnel(name)
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	err = a.DeleteTrustedCluster(name)
	if err != nil {
		if !trace.IsNotFound(err) {
			return trace.Wrap(err)
		}
	}

	return nil
}

func (s *AuthServer) sendValidateRequestToProxy(host string, validateRequest *ValidateTrustedClusterRequest) (*ValidateTrustedClusterResponse, error) {
	proxyAddr := url.URL{
		Scheme: "https",
		Host:   host,
	}

	var opts []roundtrip.ClientParam
	if s.DeveloperMode {
		log.Warn("InsecureSkipVerify used to communicate with proxy.")
		log.Warn("Make sure you intend to run Teleport in debug mode.")

		insecureWebClient := &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}
		opts = append(opts, roundtrip.HTTPClient(insecureWebClient))
	}

	clt, err := roundtrip.NewClient(proxyAddr.String(), teleport.WebAPIVersion, opts...)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	validateRequestRaw, err := validateRequest.ToRaw()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	out, err := httplib.ConvertResponse(clt.PostJSON(clt.Endpoint("webapi", "trustedclusters", "validate"), validateRequestRaw))
	if err != nil {
		return nil, trace.Wrap(err)
	}

	var validateResponseRaw *ValidateTrustedClusterResponseRaw
	err = json.Unmarshal(out.Bytes(), &validateResponseRaw)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	validateResponse, err := validateResponseRaw.ToNative()
	if err != nil {
		return nil, trace.Wrap(err)
	}

	return validateResponse, nil
}

type ValidateTrustedClusterRequest struct {
	Token string                   `json:"token"`
	CAs   []services.CertAuthority `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterRequest) ToRaw() (*ValidateTrustedClusterRequestRaw, error) {
	cas := [][]byte{}

	for _, certAuthority := range v.CAs {
		data, err := services.GetCertAuthorityMarshaler().MarshalCertAuthority(certAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, data)
	}

	return &ValidateTrustedClusterRequestRaw{
		Token: v.Token,
		CAs:   cas,
	}, nil
}

type ValidateTrustedClusterRequestRaw struct {
	Token string   `json:"token"`
	CAs   [][]byte `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterRequestRaw) ToNative() (*ValidateTrustedClusterRequest, error) {
	cas := []services.CertAuthority{}

	for _, rawCertAuthority := range v.CAs {
		certAuthority, err := services.GetCertAuthorityMarshaler().UnmarshalCertAuthority(rawCertAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, certAuthority)
	}

	return &ValidateTrustedClusterRequest{
		Token: v.Token,
		CAs:   cas,
	}, nil
}

type ValidateTrustedClusterResponse struct {
	CAs []services.CertAuthority `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterResponse) ToRaw() (*ValidateTrustedClusterResponseRaw, error) {
	cas := [][]byte{}

	for _, certAuthority := range v.CAs {
		data, err := services.GetCertAuthorityMarshaler().MarshalCertAuthority(certAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, data)
	}

	return &ValidateTrustedClusterResponseRaw{
		CAs: cas,
	}, nil
}

type ValidateTrustedClusterResponseRaw struct {
	CAs [][]byte `json:"certificate_authorities"`
}

func (v *ValidateTrustedClusterResponseRaw) ToNative() (*ValidateTrustedClusterResponse, error) {
	cas := []services.CertAuthority{}

	for _, rawCertAuthority := range v.CAs {
		certAuthority, err := services.GetCertAuthorityMarshaler().UnmarshalCertAuthority(rawCertAuthority)
		if err != nil {
			return nil, trace.Wrap(err)
		}

		cas = append(cas, certAuthority)
	}

	return &ValidateTrustedClusterResponse{
		CAs: cas,
	}, nil
}
<|endoftext|>


<commit_before>package htlcswitch

import (
	"github.com/lightningnetwork/lnd/lnwire"
)

// htlcPacket is a wrapper around htlc lnwire update, which adds additional
// information which is needed by this package.
type htlcPacket struct {
	// destNode is the first-hop destination of a local created HTLC add
	// message.
	destNode [33]byte

	// incomingChanID is the ID of the channel that we have received an incoming
	// HTLC on.
	incomingChanID lnwire.ShortChannelID

	// outgoingChanID is the ID of the channel that we have offered or will
	// offer an outgoing HTLC on.
	outgoingChanID lnwire.ShortChannelID

	// incomingHTLCID is the ID of the HTLC that we have received from the peer
	// on the incoming channel.
	incomingHTLCID uint64

	// outgoingHTLCID is the ID of the HTLC that we offered to the peer on the
	// outgoing channel.
	outgoingHTLCID uint64

	// amount is the value of the HTLC that is being created or modified.
	amount lnwire.MilliSatoshi

	// htlc lnwire message type of which depends on switch request type.
	htlc lnwire.Message

	// obfuscator contains the necessary state to allow the switch to wrap
	// any forwarded errors in an additional layer of encryption.
	obfuscator ErrorEncrypter

	// localFailure is set to true if an HTLC fails for a local payment before
	// the first hop. In this case, the failure reason is simply encoded, not
	// encrypted with any shared secret.
	localFailure bool

	// isRouted is set to true if the incomingChanID and incomingHTLCID fields
	// of a forwarded fail packet are already set and do not need to be looked
	// up in the circuit map.
	isRouted bool

	// isResolution is set to true if this packet was actually an incoming
	// resolution message from an outside sub-system. We'll treat these as
	// if they emanated directly from the switch. As a result, we'll
	// encrypt all errors related to this packet as if we were the first
	// hop.
	isResolution bool
}
<commit_msg>htlcswitch: add the incoming payment amount to the htlcPacket<commit_after>package htlcswitch

import (
	"github.com/lightningnetwork/lnd/lnwire"
)

// htlcPacket is a wrapper around htlc lnwire update, which adds additional
// information which is needed by this package.
type htlcPacket struct {
	// destNode is the first-hop destination of a local created HTLC add
	// message.
	destNode [33]byte

	// incomingChanID is the ID of the channel that we have received an incoming
	// HTLC on.
	incomingChanID lnwire.ShortChannelID

	// outgoingChanID is the ID of the channel that we have offered or will
	// offer an outgoing HTLC on.
	outgoingChanID lnwire.ShortChannelID

	// incomingHTLCID is the ID of the HTLC that we have received from the peer
	// on the incoming channel.
	incomingHTLCID uint64

	// incomingHtlcAmt is the value of the *incoming* HTLC. This will be
	// set by the link when it receives an incoming HTLC to be forwarded
	// through the switch. Then the outgoing link will use this once it
	// creates a full circuit add. This allows us to properly populate the
	// forwarding event for this circuit/packet in the case the payment
	// circuit is successful.
	incomingHtlcAmt lnwire.MilliSatoshi

	// outgoingHTLCID is the ID of the HTLC that we offered to the peer on the
	// outgoing channel.
	outgoingHTLCID uint64

	// amount is the value of the HTLC that is being created or modified.
	amount lnwire.MilliSatoshi

	// htlc lnwire message type of which depends on switch request type.
	htlc lnwire.Message

	// obfuscator contains the necessary state to allow the switch to wrap
	// any forwarded errors in an additional layer of encryption.
	obfuscator ErrorEncrypter

	// localFailure is set to true if an HTLC fails for a local payment before
	// the first hop. In this case, the failure reason is simply encoded, not
	// encrypted with any shared secret.
	localFailure bool

	// isRouted is set to true if the incomingChanID and incomingHTLCID fields
	// of a forwarded fail packet are already set and do not need to be looked
	// up in the circuit map.
	isRouted bool

	// isResolution is set to true if this packet was actually an incoming
	// resolution message from an outside sub-system. We'll treat these as
	// if they emanated directly from the switch. As a result, we'll
	// encrypt all errors related to this packet as if we were the first
	// hop.
	isResolution bool
}
<|endoftext|>


<commit_before>package magicsql_test

import (
	"fmt"

	"github.com/Nerdmaster/magicsql"
	_ "github.com/mattn/go-sqlite3"
)

func getdb() *magicsql.DB {
	// Set up a simple sqlite database
	var db, err = magicsql.Open("sqlite3", "./test.db")
	if err != nil {
		panic(err)
	}

	var sqlStmt = `
		CREATE TABLE IF NOT EXISTS foos (
			id   INTEGER NOT NULL PRIMARY KEY,
			one  TEXT,
			two  INT,
			tree BOOL,
			four INT
		);
		DELETE FROM foos;
	`
	_, err = db.DataSource().Exec(sqlStmt)
	if err != nil {
		panic(err)
	}

	return db
}

// This example showcases how you can use magicsql to simplify database access
// without relying on magically generated SQL or structure registration
func Example_withoutMagic() {
	// Start an operation
	var db = getdb()
	var op = db.Operation()

	var count = -1
	var rows = op.Query("SELECT count(*) FROM foos")
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Printf("Row count at start: %d\n", count)

	// Create a transaction because hey, why not?
	op.BeginTransaction()
	var stmt = op.Prepare("INSERT INTO foos (one,two,tree,four) VALUES (?, ?, ?, ?)")
	stmt.Exec("one", 2, true, 4)
	stmt.Exec("thing", 5, false, 7)
	stmt.Exec("blargh", 1, true, 5)
	stmt.Exec("sploop", 2, true, 4)

	// Instead of calling commit/rollback, we let the transaction figure it out
	// based on its error state
	op.EndTransaction()

	// Create a transaction and force it to fail
	op.BeginTransaction()
	stmt = op.Prepare("INSERT INTO foos (one,two,tree,four) VALUES (?, ?, ?, ?)")
	stmt.Exec("one+", 2, true, 4)
	stmt.Exec("thing+", 5, false, 7)

	rows = op.Query("SELECT COUNT(*) FROM foos")
	count = -1
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Println("Count in transaction:", count)

	op.SetErr(fmt.Errorf("forcing rollback"))
	op.EndTransaction()

	// Reset the error state so we can continue working
	op.Reset()

	rows = op.Query("SELECT COUNT(*) FROM foos")
	count = -1
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Println("Count after forced rollback:", count)

	rows = op.Query("SELECT one,two,tree,four FROM foos WHERE two > 1", true)
	var one string
	var two, four int
	var tree bool
	for rows.Next() {
		rows.Scan(&one, &two, &tree, &four)
		fmt.Printf("one: %s, two: %d, tree: %#v, four: %d\n", one, two, tree, four)
	}

	// Output:
	// Row count at start: 0
	// Count in transaction: 6
	// Count after forced rollback: 4
	// one: one, two: 2, tree: true, four: 4
	// one: thing, two: 5, tree: false, four: 7
	// one: sploop, two: 2, tree: true, four: 4
}
<commit_msg>Close statements prepared in test<commit_after>package magicsql_test

import (
	"fmt"

	"github.com/Nerdmaster/magicsql"
	_ "github.com/mattn/go-sqlite3"
)

func getdb() *magicsql.DB {
	// Set up a simple sqlite database
	var db, err = magicsql.Open("sqlite3", "./test.db")
	if err != nil {
		panic(err)
	}

	var sqlStmt = `
		CREATE TABLE IF NOT EXISTS foos (
			id   INTEGER NOT NULL PRIMARY KEY,
			one  TEXT,
			two  INT,
			tree BOOL,
			four INT
		);
		DELETE FROM foos;
	`
	_, err = db.DataSource().Exec(sqlStmt)
	if err != nil {
		panic(err)
	}

	return db
}

// This example showcases how you can use magicsql to simplify database access
// without relying on magically generated SQL or structure registration
func Example_withoutMagic() {
	// Start an operation
	var db = getdb()
	var op = db.Operation()

	var count = -1
	var rows = op.Query("SELECT count(*) FROM foos")
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Printf("Row count at start: %d\n", count)

	// Create a transaction because hey, why not?
	op.BeginTransaction()
	var stmt = op.Prepare("INSERT INTO foos (one,two,tree,four) VALUES (?, ?, ?, ?)")
	stmt.Exec("one", 2, true, 4)
	stmt.Exec("thing", 5, false, 7)
	stmt.Exec("blargh", 1, true, 5)
	stmt.Exec("sploop", 2, true, 4)
	stmt.Close()

	// Instead of calling commit/rollback, we let the transaction figure it out
	// based on its error state
	op.EndTransaction()

	// Create a transaction and force it to fail
	op.BeginTransaction()
	stmt = op.Prepare("INSERT INTO foos (one,two,tree,four) VALUES (?, ?, ?, ?)")
	stmt.Exec("one+", 2, true, 4)
	stmt.Exec("thing+", 5, false, 7)
	stmt.Close()

	rows = op.Query("SELECT COUNT(*) FROM foos")
	count = -1
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Println("Count in transaction:", count)

	op.SetErr(fmt.Errorf("forcing rollback"))
	op.EndTransaction()

	// Reset the error state so we can continue working
	op.Reset()

	rows = op.Query("SELECT COUNT(*) FROM foos")
	count = -1
	for rows.Next() {
		rows.Scan(&count)
	}
	fmt.Println("Count after forced rollback:", count)

	rows = op.Query("SELECT one,two,tree,four FROM foos WHERE two > 1", true)
	var one string
	var two, four int
	var tree bool
	for rows.Next() {
		rows.Scan(&one, &two, &tree, &four)
		fmt.Printf("one: %s, two: %d, tree: %#v, four: %d\n", one, two, tree, four)
	}

	// Output:
	// Row count at start: 0
	// Count in transaction: 6
	// Count after forced rollback: 4
	// one: one, two: 2, tree: true, four: 4
	// one: thing, two: 5, tree: false, four: 7
	// one: sploop, two: 2, tree: true, four: 4
}
<|endoftext|>


<commit_before>package constants

const (
	SubPortNumber                = 6969
	DominatorPortNumber          = 6970
	ImageServerPortNumber        = 6971
	BasicFileGenServerPortNumber = 6972
	SimpleMdbServerPortNumber    = 6973
	ImageUnpackerPortNumber      = 6974
	ImaginatorPortNumber         = 6975
	HypervisorPortNumber         = 6976
	FleetManagerPortNumber       = 6977

	DefaultCpuPercent          = 50
	DefaultNetworkSpeedPercent = 10
	DefaultScanSpeedPercent    = 2

	AssignedOIDBase        = "1.3.6.1.4.1.9586.100.7"
	PermittedMethodListOID = AssignedOIDBase + ".1"
)

var RequiredPaths = map[string]rune{
	"/etc":        'd',
	"/etc/passwd": 'f',
	"/usr":        'd',
	"/usr/bin":    'd',
}

var ScanExcludeList = []string{
	"/home/.*",
	"/tmp/.*",
	"/var/log/.*",
	"/var/mail/.*",
	"/var/spool/.*",
	"/var/tmp/.*",
}
<commit_msg>Add lib/constants.GroupListOID.<commit_after>package constants

const (
	SubPortNumber                = 6969
	DominatorPortNumber          = 6970
	ImageServerPortNumber        = 6971
	BasicFileGenServerPortNumber = 6972
	SimpleMdbServerPortNumber    = 6973
	ImageUnpackerPortNumber      = 6974
	ImaginatorPortNumber         = 6975
	HypervisorPortNumber         = 6976
	FleetManagerPortNumber       = 6977

	DefaultCpuPercent          = 50
	DefaultNetworkSpeedPercent = 10
	DefaultScanSpeedPercent    = 2

	AssignedOIDBase        = "1.3.6.1.4.1.9586.100.7"
	PermittedMethodListOID = AssignedOIDBase + ".1"
	GroupListOID           = AssignedOIDBase + ".2"
)

var RequiredPaths = map[string]rune{
	"/etc":        'd',
	"/etc/passwd": 'f',
	"/usr":        'd',
	"/usr/bin":    'd',
}

var ScanExcludeList = []string{
	"/home/.*",
	"/tmp/.*",
	"/var/log/.*",
	"/var/mail/.*",
	"/var/spool/.*",
	"/var/tmp/.*",
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2021 PaperCut Software International Pty. Ltd.
 */

package updaterconf_test

import (
	"github.com/papercutsoftware/silver/lib/osutils"
	"github.com/papercutsoftware/silver/service/config"
	"github.com/papercutsoftware/silver/service/updaterconf"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

func TestUpdateDisableEnableRoundTrip(t *testing.T) {
	str := `{
	"ServiceDescription": {
        "DisplayName": "The Simple Service",
        "Description": "Simple service with update"
    },
    "Services": [
        {
            "Path": "simple-server.exe"
        }
    ],
	"StartupTasks": [
        {
            "Path": "my-updater.exe",
            "Args": ["https://my-update.acme.com/product-version/v1/check-update/"]
        }
	]
}`
	file, err := createConf(str)
	if err != nil {
		t.Errorf("Failed to create conf file: %v", err)
	}
	defer func() { _ = os.Remove(file.Name()) }()

	silverDir := filepath.Dir(file.Name())
	updaterConf, err := updaterconf.Create(silverDir, file.Name(), "my-updater")
	if err != nil {
		t.Errorf("Failed to create updater: %v", err)
	}
	if !osutils.FileExists(filepath.Join(silverDir, "backup-"+filepath.Base(file.Name()))) {
		t.Errorf("Expected silver config file to be backed up")
	}

	if err = updaterConf.DisableAutoUpdate(); err != nil {
		t.Errorf("Failed to disable update: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if enabled {
		t.Errorf("Expected update to be disabled")
	}

	if conf, err := config.LoadConfigNoReplacements(file.Name()); err != nil {
		t.Errorf("Failed to load updated config: %v", err)
	} else if len(conf.StartupTasks) > 0 {
		t.Errorf("Expected all updated tasks to be removed")
	}
	_ = os.Remove(filepath.Join(silverDir, config.ReloadFileName))

	if err = updaterConf.EnableAutoUpdates(); err != nil {
		t.Errorf("Failed to enable update: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if !enabled {
		t.Errorf("Expected update to be enabled")
	}
}

func TestConfWithoutUpdates(t *testing.T) {
	str := `{
	"ServiceDescription": {
        "DisplayName": "The Simple Service",
        "Description": "Simple service without update"
    },
    "Services": [
        {
            "Path": "simple-server.exe"
        }
    ],
	"StartupTasks": [
        {
            "Path": "not-updater.exe",
            "Args": ["foo"]
        }
	]
}`
	file, err := createConf(str)
	if err != nil {
		t.Errorf("Failed to create conf file: %v", err)
	}
	defer func() { _ = os.Remove(file.Name()) }()

	silverDir := filepath.Dir(file.Name())
	updaterConf, err := updaterconf.Create(silverDir, file.Name(), "my-updater")
	if err != nil {
		t.Errorf("Failed to create updater: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if enabled {
		t.Errorf("Expected update to be disabled")
	}

	if err = updaterConf.DisableAutoUpdate(); err != nil {
		t.Errorf("Failed to disable update: %v", err)
	}

	if conf, err := config.LoadConfigNoReplacements(file.Name()); err != nil {
		t.Errorf("Failed to load updated config: %v", err)
	} else if len(conf.StartupTasks) != 1 {
		t.Errorf("Expected all updated tasks to be left")
	}
}

func createConf(data string) (*os.File, error) {
	file, err := ioutil.TempFile("", "test-silver-config")
	if err != nil {
		return nil, err
	}
	err = osutils.WriteFileString(file.Name(), data, 0600)
	if err != nil {
		return nil, err
	}
	return file, err
}
<commit_msg>fix: add more tests<commit_after>/*
 * Copyright © 2021 PaperCut Software International Pty. Ltd.
 */

package updaterconf_test

import (
	"github.com/papercutsoftware/silver/lib/osutils"
	"github.com/papercutsoftware/silver/service/config"
	"github.com/papercutsoftware/silver/service/updaterconf"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

func TestUpdateDisableEnableRoundTrip(t *testing.T) {
	str := `{
	"ServiceDescription": {
        "DisplayName": "The Simple Service",
        "Description": "Simple service with update"
    },
    "Services": [
        {
            "Path": "simple-server.exe"
        }
    ],
	"StartupTasks": [
        {
            "Path": "my-updater.exe",
            "Args": ["https://my-update.acme.com/product-version/v1/check-update/"]
        }
	]
}`
	file, err := createConf(str)
	if err != nil {
		t.Errorf("Failed to create conf file: %v", err)
	}
	defer func() { _ = os.Remove(file.Name()) }()

	silverDir := filepath.Dir(file.Name())
	updaterConf, err := updaterconf.Create(silverDir, file.Name(), "my-updater")
	if err != nil {
		t.Errorf("Failed to create updater: %v", err)
	}
	if !osutils.FileExists(filepath.Join(silverDir, "backup-"+filepath.Base(file.Name()))) {
		t.Errorf("Expected silver config file to be backed up")
	}

	if err = updaterConf.DisableAutoUpdate(); err != nil {
		t.Errorf("Failed to disable update: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if enabled {
		t.Errorf("Expected update to be disabled")
	}

	if conf, err := config.LoadConfigNoReplacements(file.Name()); err != nil {
		t.Errorf("Failed to load updated config: %v", err)
	} else if len(conf.StartupTasks) > 0 {
		t.Errorf("Expected all updated tasks to be removed")
	}
	_ = os.Remove(filepath.Join(silverDir, config.ReloadFileName))

	if err = updaterConf.EnableAutoUpdates(); err != nil {
		t.Errorf("Failed to enable update: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if !enabled {
		t.Errorf("Expected update to be enabled")
	}
}

func TestConfWithoutUpdates(t *testing.T) {
	str := `{
	"ServiceDescription": {
        "DisplayName": "The Simple Service",
        "Description": "Simple service without update"
    },
    "Services": [
        {
            "Path": "simple-server.exe"
        }
    ],
	"StartupTasks": [
        {
            "Path": "not-updater.exe",
            "Args": ["foo"]
        }
	]
}`
	file, err := createConf(str)
	if err != nil {
		t.Errorf("Failed to create conf file: %v", err)
	}
	defer func() { _ = os.Remove(file.Name()) }()

	silverDir := filepath.Dir(file.Name())
	updaterConf, err := updaterconf.Create(silverDir, file.Name(), "my-updater")
	if err != nil {
		t.Errorf("Failed to create updater: %v", err)
	}

	if enabled, err := updaterConf.IsAutoUpdateEnabled(); err != nil {
		t.Errorf("Failed to check if update is enabled: %v", err)
	} else if enabled {
		t.Errorf("Expected update to be disabled")
	}

	if err = updaterConf.DisableAutoUpdate(); err != nil {
		t.Errorf("Failed to disable update: %v", err)
	}

	if conf, err := config.LoadConfigNoReplacements(file.Name()); err != nil {
		t.Errorf("Failed to load updated config: %v", err)
	} else if len(conf.StartupTasks) != 1 {
		t.Errorf("Expected all updated tasks to be left")
	}
}

func createConf(data string) (*os.File, error) {
	file, err := ioutil.TempFile("", "test-silver-config")
	if err != nil {
		return nil, err
	}
	err = osutils.WriteFileString(file.Name(), data, 0600)
	if err != nil {
		return nil, err
	}
	return file, err
}

func TestCreateArgs(t *testing.T) {
	file, err := createConf(`{
    "ServiceDescription": {
        "DisplayName": "The Simple Service",
        "Description": "Only does one thing, but does it well!"
    },
    "Services": [{
            "Path": "simple-server.exe"
        }]}`)
	if err != nil {
		t.Errorf("Failed to create conf file: %v", err)
	}
	defer func() { _ = os.Remove(file.Name()) }()

	type args struct {
		silverDir            string
		silverConfigFilename string
		updaterFilename      string
	}
	tests := []struct {
		name    string
		args    args
		want    *updaterconf.UpdaterConf
		wantErr bool
	}{
		{
			name: "Test updater create invalid args",
			args: args{
				silverDir:            "xyz",
				silverConfigFilename: "not-found",
				updaterFilename:      "foobar",
			},
			wantErr: true,
		},
		{
			name: "Test updater create invalid empty dir",
			args: args{
				silverDir:            "",
				silverConfigFilename: "not-found",
				updaterFilename:      "",
			},
			wantErr: true,
		},
		{
			name: "Test updater create invalid empty conf",
			args: args{
				silverDir:            ".",
				silverConfigFilename: "",
				updaterFilename:      "",
			},
			wantErr: true,
		},
		{
			name: "Test updater create with valid args",
			args: args{
				silverDir:            filepath.Dir(file.Name()),
				silverConfigFilename: filepath.Base(file.Name()),
				updaterFilename:      "",
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := updaterconf.Create(tt.args.silverDir, tt.args.silverConfigFilename, tt.args.updaterFilename)
			if (err != nil) != tt.wantErr {
				t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if err == nil && got == nil {
				t.Errorf("Create expected to return non nil")
			}
		})
	}
}
<|endoftext|>


<commit_before>package editor

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/hacdias/caddy-hugo/config"
	"github.com/hacdias/caddy-hugo/utils"
	"github.com/robfig/cron"
	"github.com/spf13/cast"
	"github.com/spf13/hugo/parser"
)

// POST handles the POST method on editor page
func POST(w http.ResponseWriter, r *http.Request, c *config.Config, filename string) (int, error) {
	// Get the JSON information sent using a buffer
	rawBuffer := new(bytes.Buffer)
	rawBuffer.ReadFrom(r.Body)

	// Creates the raw file "map" using the JSON
	var rawFile map[string]interface{}
	json.Unmarshal(rawBuffer.Bytes(), &rawFile)

	// Initializes the file content to write
	var file []byte

	switch r.Header.Get("X-Content-Type") {
	case "frontmatter-only":
		f, code, err := parseFrontMatterOnlyFile(rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	case "content-only":
		// The main content of the file
		mainContent := rawFile["content"].(string)
		mainContent = "\n\n" + strings.TrimSpace(mainContent)

		file = []byte(mainContent)
	case "complete":
		f, code, err := parseCompleteFile(r, c, rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	default:
		return http.StatusBadRequest, errors.New("X-Content-Type header not defined")
	}

	// Write the file
	err := ioutil.WriteFile(filename, file, 0666)

	if err != nil {
		w.Write([]byte(err.Error()))
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return http.StatusOK, nil
}

func parseFrontMatterOnlyFile(rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
	var mark rune

	switch frontmatter {
	case "toml":
		mark = rune('+')
	case "json":
		mark = rune('{')
	case "yaml":
		mark = rune('-')
	default:
		return []byte{}, http.StatusBadRequest, errors.New("Can't define the frontmatter.")
	}

	f, err := parser.InterfaceToFrontMatter(rawFile, mark)
	fString := string(f)

	// If it's toml or yaml, strip frontmatter identifier
	if frontmatter == "toml" {
		fString = strings.TrimSuffix(fString, "+++\n")
		fString = strings.TrimPrefix(fString, "+++\n")
	}

	if frontmatter == "yaml" {
		fString = strings.TrimSuffix(fString, "---\n")
		fString = strings.TrimPrefix(fString, "---\n")
	}

	f = []byte(fString)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	return f, http.StatusOK, nil
}

func parseCompleteFile(r *http.Request, c *config.Config, rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	// The main content of the file
	mainContent := rawFile["content"].(string)
	mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n"

	// Removes the main content from the rest of the frontmatter
	delete(rawFile, "content")

	// Schedule the post
	if r.Header.Get("X-Schedule") == "true" {
		t := cast.ToTime(rawFile["date"].(string))

		scheduler := cron.New()
		scheduler.AddFunc(t.In(time.Now().Location()).Format("05 04 15 02 01 *"), func() {
			// Set draft to false
			rawFile["draft"] = false

			// Converts the frontmatter in JSON
			jsonFrontmatter, err := json.Marshal(rawFile)

			if err != nil {
				return
			}

			// Indents the json
			frontMatterBuffer := new(bytes.Buffer)
			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

			// Generates the final file
			f := new(bytes.Buffer)
			f.Write(frontMatterBuffer.Bytes())
			f.Write([]byte(mainContent))
			file := f.Bytes()

			// Write the file
			if err = ioutil.WriteFile(filename, file, 0666); err != nil {
				return
			}

			utils.Run(c)
		})
		scheduler.Start()
	}

	// Converts the frontmatter in JSON
	jsonFrontmatter, err := json.Marshal(rawFile)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	// Indents the json
	frontMatterBuffer := new(bytes.Buffer)
	json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

	// Generates the final file
	f := new(bytes.Buffer)
	f.Write(frontMatterBuffer.Bytes())
	f.Write([]byte(mainContent))
	return f.Bytes(), http.StatusOK, nil
}
<commit_msg>fix blank space -> begin of files<commit_after>package editor

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/hacdias/caddy-hugo/config"
	"github.com/hacdias/caddy-hugo/utils"
	"github.com/robfig/cron"
	"github.com/spf13/cast"
	"github.com/spf13/hugo/parser"
)

// POST handles the POST method on editor page
func POST(w http.ResponseWriter, r *http.Request, c *config.Config, filename string) (int, error) {
	// Get the JSON information sent using a buffer
	rawBuffer := new(bytes.Buffer)
	rawBuffer.ReadFrom(r.Body)

	// Creates the raw file "map" using the JSON
	var rawFile map[string]interface{}
	json.Unmarshal(rawBuffer.Bytes(), &rawFile)

	// Initializes the file content to write
	var file []byte

	switch r.Header.Get("X-Content-Type") {
	case "frontmatter-only":
		f, code, err := parseFrontMatterOnlyFile(rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	case "content-only":
		// The main content of the file
		mainContent := rawFile["content"].(string)
		mainContent = strings.TrimSpace(mainContent)

		file = []byte(mainContent)
	case "complete":
		f, code, err := parseCompleteFile(r, c, rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	default:
		return http.StatusBadRequest, errors.New("X-Content-Type header not defined")
	}

	// Write the file
	err := ioutil.WriteFile(filename, file, 0666)

	if err != nil {
		w.Write([]byte(err.Error()))
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return http.StatusOK, nil
}

func parseFrontMatterOnlyFile(rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
	var mark rune

	switch frontmatter {
	case "toml":
		mark = rune('+')
	case "json":
		mark = rune('{')
	case "yaml":
		mark = rune('-')
	default:
		return []byte{}, http.StatusBadRequest, errors.New("Can't define the frontmatter.")
	}

	f, err := parser.InterfaceToFrontMatter(rawFile, mark)
	fString := string(f)

	// If it's toml or yaml, strip frontmatter identifier
	if frontmatter == "toml" {
		fString = strings.TrimSuffix(fString, "+++\n")
		fString = strings.TrimPrefix(fString, "+++\n")
	}

	if frontmatter == "yaml" {
		fString = strings.TrimSuffix(fString, "---\n")
		fString = strings.TrimPrefix(fString, "---\n")
	}

	f = []byte(fString)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	return f, http.StatusOK, nil
}

func parseCompleteFile(r *http.Request, c *config.Config, rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	// The main content of the file
	mainContent := rawFile["content"].(string)
	mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n"

	// Removes the main content from the rest of the frontmatter
	delete(rawFile, "content")

	// Schedule the post
	if r.Header.Get("X-Schedule") == "true" {
		t := cast.ToTime(rawFile["date"].(string))

		scheduler := cron.New()
		scheduler.AddFunc(t.In(time.Now().Location()).Format("05 04 15 02 01 *"), func() {
			// Set draft to false
			rawFile["draft"] = false

			// Converts the frontmatter in JSON
			jsonFrontmatter, err := json.Marshal(rawFile)

			if err != nil {
				return
			}

			// Indents the json
			frontMatterBuffer := new(bytes.Buffer)
			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

			// Generates the final file
			f := new(bytes.Buffer)
			f.Write(frontMatterBuffer.Bytes())
			f.Write([]byte(mainContent))
			file := f.Bytes()

			// Write the file
			if err = ioutil.WriteFile(filename, file, 0666); err != nil {
				return
			}

			utils.Run(c)
		})
		scheduler.Start()
	}

	// Converts the frontmatter in JSON
	jsonFrontmatter, err := json.Marshal(rawFile)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	// Indents the json
	frontMatterBuffer := new(bytes.Buffer)
	json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

	// Generates the final file
	f := new(bytes.Buffer)
	f.Write(frontMatterBuffer.Bytes())
	f.Write([]byte(mainContent))
	return f.Bytes(), http.StatusOK, nil
}
<|endoftext|>