<commit_before>// Copyright (c) 2014-2015 PPCD developers.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
	"io"
)

// MsgCheckPoint implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgCheckPoint struct {
	// Cmd is the command for the message which was rejected such as
	// as CmdBlock or CmdTx.  This can be obtained from the Command function
	// of a Message.
	Cmd string

	// Hash identifies a specific block or transaction that was rejected
	// and therefore only applies the MsgBlock and MsgTx messages.
	Hash ShaHash
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcDecode(r io.Reader, pver uint32) error {

	// Command that was rejected.
	cmd, err := readVarString(r, pver)
	if err != nil {
		return err
	}
	msg.Cmd = cmd

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = readElement(r, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcEncode(w io.Writer, pver uint32) error {

	// Command that was rejected.
	err := writeVarString(w, pver, msg.Cmd)
	if err != nil {
		return err
	}

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = writeElement(w, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// Command returns the protocol command string for the message.  This is part
// of the Message interface implementation.
func (msg *MsgCheckPoint) Command() string {
	return CmdCheckPoint
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver.  This is part of the Message interface implementation.
func (msg *MsgCheckPoint) MaxPayloadLength(pver uint32) uint32 {
	plen := MaxMessagePayload
	return uint32(plen)
}

// NewMsgCheckPoint returns a new bitcoin reject message that conforms to the
// Message interface.  See MsgCheckPoint for details.
func NewMsgCheckPoint(command string) *MsgCheckPoint {
	return &MsgCheckPoint{
		Cmd: command,
	}
}
<commit_msg>fix ppc_msgcheckpoint.go<commit_after>// Copyright (c) 2014-2015 PPCD developers.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
	"io"

	"github.com/ppcsuite/ppcd/chaincfg/chainhash"
)

// MsgCheckPoint implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgCheckPoint struct {
	// Cmd is the command for the message which was rejected such as
	// as CmdBlock or CmdTx.  This can be obtained from the Command function
	// of a Message.
	Cmd string

	// Hash identifies a specific block or transaction that was rejected
	// and therefore only applies the MsgBlock and MsgTx messages.
	Hash chainhash.Hash
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcDecode(r io.Reader, pver uint32) error {

	// Command that was rejected.
	cmd, err := readVarString(r, pver)
	if err != nil {
		return err
	}
	msg.Cmd = cmd

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = readElement(r, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcEncode(w io.Writer, pver uint32) error {

	// Command that was rejected.
	err := writeVarString(w, pver, msg.Cmd)
	if err != nil {
		return err
	}

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = writeElement(w, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// Command returns the protocol command string for the message.  This is part
// of the Message interface implementation.
func (msg *MsgCheckPoint) Command() string {
	return CmdCheckPoint
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver.  This is part of the Message interface implementation.
func (msg *MsgCheckPoint) MaxPayloadLength(pver uint32) uint32 {
	plen := MaxMessagePayload
	return uint32(plen)
}

// NewMsgCheckPoint returns a new bitcoin reject message that conforms to the
// Message interface.  See MsgCheckPoint for details.
func NewMsgCheckPoint(command string) *MsgCheckPoint {
	return &MsgCheckPoint{
		Cmd: command,
	}
}
<|endoftext|>


<commit_before>/*
Package quadtree implements a quadtree

Currently, it only stores points, not ancillary data. This could be trivially changed by adding another variable to the Point class, or changing the Quadtree struct to contain a map rather than slice of Points, or changing the Quadtree struct to contain a PointWrapper which contains a point and a data value.

quadtree is not currently safe for concurrent use.
*/
package quadtree

import (
	"fmt"
	"strconv"
	"unsafe"
	"sync/atomic"
)

type Point struct {
	X float64
	Y float64
}

func (p *Point) String() string {
	return "[" + strconv.FormatFloat(p.X, 'f', -1, 64) + "," + strconv.FormatFloat(p.Y, 'f', -1, 64) + "]"
}

type BoundingBox struct {
	Center        Point
	HalfDimension Point
}

func (b *BoundingBox) Contains(p *Point) bool {
	return p.X >= b.Center.X-b.HalfDimension.X &&
		p.X <= b.Center.X+b.HalfDimension.X &&
		p.Y >= b.Center.Y-b.HalfDimension.Y &&
		p.Y <= b.Center.Y+b.HalfDimension.Y
}

func (b *BoundingBox) Intersects(other *BoundingBox) bool {
	return b.Center.X+b.HalfDimension.X > other.Center.X-other.HalfDimension.X &&
		b.Center.X-b.HalfDimension.X < other.Center.X+other.HalfDimension.X &&
		b.Center.Y+b.HalfDimension.Y > other.Center.Y-other.HalfDimension.Y &&
		b.Center.Y-b.HalfDimension.Y < other.Center.Y+other.HalfDimension.Y
}

type Quadtree struct {
	Boundary *BoundingBox
	Points   *[]Point
	Nw       *Quadtree
	Ne       *Quadtree
	Sw       *Quadtree
	Se       *Quadtree
}

func (q *Quadtree) Insert(p Point) bool {
	// we don't need to check the boundary within the CAS loop, because it can't change.
	// if the quadtree were changed to allow changing the Boundary, this would no longer be threadsafe.
	if !q.Boundary.Contains(&p) {
		return false
	}

	for {
		// the value we start working with
		oldPoints := q.Points
		// if at any point in our attempts to add the point, the length becomes the capacity, break so we can subdivide if necessary and add to a subtree
		if len(*oldPoints) >= cap(*oldPoints) {
			break
		}
		newPoints := append(*oldPoints, p)
		// if the working value is the same, set the new slice with our point
		ok := atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Points)), unsafe.Pointer(oldPoints), unsafe.Pointer(&newPoints))
		if ok {
			// the CAS succeeded, our point was added, return success
			return true
		}
		fmt.Println("CAS Insert failed: len(points): " + strconv.Itoa(len(newPoints)))
		// if the working value changed underneath us, loop and try again
	}

	// If we get here, we broke the loop because the length exceeds the capacity.
	// We must now Subdivide if necessary, and add the point to the proper subtree

	// at this point, with the above CAS, even if we simply mutex the Subdivide(), we will have achieved amortized lock-free time.

	// subdivide is threadsafe. The function itself does CAS
	q.subdivide()

	// These inserts are themselves threadsafe. Therefore, we don't need to do any special CAS work here.
	return q.Nw.Insert(p) ||
		q.Ne.Insert(p) ||
		q.Sw.Insert(p) ||
		q.Se.Insert(p)
}

// helper function of Quadtree.insert()
// subdivides the tree into quadrants.
// This should be called when the capacity is exceeded.
func (q *Quadtree) subdivide() {
	if *q.Points == nil {
		return
	}
	if q.Nw == nil {
		q.createNw()
	}
	if q.Ne == nil {
		q.createNe()
	}
	if q.Sw == nil {
		q.createSw()
	}
	if q.Se == nil {
		q.createSe()
	}
	if *q.Points != nil {
		q.disperse()
	}
}

// helper function for Quadtree.subdivide()
//
// places all points in the tree in the appropriate quadrant,
// and clears the points of this tree.
func (q *Quadtree) disperse() {
	for {
		oldPoints := q.Points
		if len(*oldPoints) == 0 {
			break
		}
		p := (*oldPoints)[0]
		newPoints := oldPoints
		if len(*oldPoints) == 1 {
			*newPoints = nil
		} else {
			*newPoints = (*oldPoints)[1:]
		}
		ok := atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Points)), unsafe.Pointer(oldPoints), unsafe.Pointer(newPoints))
		if !ok {
			fmt.Println("CAS disperse failed: len(points): " + strconv.Itoa(len(*newPoints)))
			continue
		}
		ok = q.Nw.Insert(p) || q.Ne.Insert(p) || q.Sw.Insert(p) || q.Se.Insert(p)
		// @todo remove when quadtree is finished. Useful for debugging, but it slows things down
		if !ok {
			panic("quadtree contained a point outside boundary")
		}
	}
	*q.Points = nil
}

// helper function for Quadtree.createDir() functions
// creates a quadrant of the current Quadtree, with the given center
func (q *Quadtree) createQuadrant(center Point) *Quadtree {
	points := make([]Point, 0, cap(*q.Points))
	return &Quadtree{
		Boundary: &BoundingBox{
			Center: center,
			HalfDimension: Point{q.Boundary.HalfDimension.X / 2.0, q.Boundary.HalfDimension.Y / 2.0},
		},
		Points: &points,
	}
}

// helper function for Quadtree.subdivide()
// creates the Nw quadrant of the tree
func (q *Quadtree) createNw() {
	center := Point{
		X: q.Boundary.Center.X - q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y - q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Nw)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Ne quadrant of the tree
func (q *Quadtree) createNe() {
	center := Point{
		X: q.Boundary.Center.X + q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y - q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Ne)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Sw quadrant of the tree
func (q *Quadtree) createSw() {
	center := Point{
		X: q.Boundary.Center.X - q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y + q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Sw)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Se quadrant of the tree
func (q *Quadtree) createSe() {
	center := Point{
		X: q.Boundary.Center.X + q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y + q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Se)), nil, unsafe.Pointer(quadrant))
}

func (q *Quadtree) QueryRange(b *BoundingBox) []Point {
	var points []Point
	if !q.Boundary.Intersects(b) {
		return nil
	}
	for _, point := range *q.Points {
		if b.Contains(&point) {
			points = append(points, point)
		}
	}
	if q.Nw != nil {
		points = append(points, q.Nw.QueryRange(b)...)
	}
	if q.Ne != nil {
		points = append(points, q.Ne.QueryRange(b)...)
	}
	if q.Sw != nil {
		points = append(points, q.Sw.QueryRange(b)...)
	}
	if q.Se != nil {
		points = append(points, q.Se.QueryRange(b)...)
	}
	return points
}

func NewQuadTree(b *BoundingBox, capacity int) *Quadtree {
	points := make([]Point, 0, capacity)
	return &Quadtree {
		Boundary: b,
		Points: &points,
//		Nw: &Quadtree{},
//		Ne: &Quadtree{},
//		Sw: &Quadtree{},
//		Se: &Quadtree{},
	}
}
<commit_msg>changed concurrent quadtree to use linked lists from slices<commit_after>/*
Package quadtree implements a quadtree

Currently, it only stores points, not ancillary data. This could be trivially changed by adding another variable to the Point class, or changing the Quadtree struct to contain a map rather than slice of Points, or changing the Quadtree struct to contain a PointWrapper which contains a point and a data value.

quadtree is not currently safe for concurrent use.
*/
package quadtree

import (
	"fmt"
	"strconv"
	"unsafe"
	"sync/atomic"
)

type Point struct {
	X float64
	Y float64
}

func (p *Point) String() string {
	return "[" + strconv.FormatFloat(p.X, 'f', -1, 64) + "," + strconv.FormatFloat(p.Y, 'f', -1, 64) + "]"
}

type BoundingBox struct {
	Center        Point
	HalfDimension Point
}

func (b *BoundingBox) Contains(p *Point) bool {
	return p.X >= b.Center.X-b.HalfDimension.X &&
		p.X <= b.Center.X+b.HalfDimension.X &&
		p.Y >= b.Center.Y-b.HalfDimension.Y &&
		p.Y <= b.Center.Y+b.HalfDimension.Y
}

func (b *BoundingBox) Intersects(other *BoundingBox) bool {
	return b.Center.X+b.HalfDimension.X > other.Center.X-other.HalfDimension.X &&
		b.Center.X-b.HalfDimension.X < other.Center.X+other.HalfDimension.X &&
		b.Center.Y+b.HalfDimension.Y > other.Center.Y-other.HalfDimension.Y &&
		b.Center.Y-b.HalfDimension.Y < other.Center.Y+other.HalfDimension.Y
}

type PointListNode struct {
	*Point
	Next *PointListNode
}

func NewPointListNode(point *Point, next *PointListNode) *PointListNode {
	return &PointListNode {
		Point: point,
		Next: next,
	}
}

type PointList struct {
	First *PointListNode
	Capacity int
	Length int // this is a cache for speed; it could be calculated from the PointsList
}

func NewPointList(capacity int) *PointList {
	return &PointList {
		First: nil,
		Capacity: capacity,
	}
}

type Quadtree struct {
	Boundary *BoundingBox
	Points   *PointList
	Nw       *Quadtree
	Ne       *Quadtree
	Sw       *Quadtree
	Se       *Quadtree
}

func (q *Quadtree) Insert(p *Point) bool {
	// we don't need to check the boundary within the CAS loop, because it can't change.
	// if the quadtree were changed to allow changing the Boundary, this would no longer be threadsafe.
	if !q.Boundary.Contains(p) {
//		fmt.Println("insert outside boundary")
		return false
	}
	for {
		// the value we start working with
		oldPoints := q.Points
		// if at any point in our attempts to add the point, the length becomes the capacity, break so we can subdivide if necessary and add to a subtree
		if oldPoints == nil || oldPoints.Length >= oldPoints.Capacity {
			break
		}
		newPoints := *oldPoints
		newPoints.First = NewPointListNode(p, newPoints.First)
		newPoints.Length++
		// if the working value is the same, set the new slice with our point
		ok := atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Points)), unsafe.Pointer(oldPoints), unsafe.Pointer(&newPoints))
		if ok {
			// the CAS succeeded, our point was added, return success
			return true
		}
//		fmt.Println("CAS Insert failed: len(points): " + strconv.Itoa(newPoints.Length))
		// if the working value changed underneath us, loop and try again
	}

	// If we get here, we broke the loop because the length exceeds the capacity.
	// We must now Subdivide if necessary, and add the point to the proper subtree

	// at this point, with the above CAS, even if we simply mutex the Subdivide(), we will have achieved amortized lock-free time.

	// subdivide is threadsafe. The function itself does CAS
	if q.Points != nil {
		q.subdivide()
	}

	// These inserts are themselves threadsafe. Therefore, we don't need to do any special CAS work here.
	return q.Nw.Insert(p) ||
		q.Ne.Insert(p) ||
		q.Sw.Insert(p) ||
		q.Se.Insert(p)
}

// helper function of Quadtree.insert()
// subdivides the tree into quadrants.
// This should be called when the capacity is exceeded.
func (q *Quadtree) subdivide() {
	if q.Points == nil {
		return
	}
	if q.Nw == nil {
		q.createNw()
	}
	if q.Ne == nil {
		q.createNe()
	}
	if q.Sw == nil {
		q.createSw()
	}
	if q.Se == nil {
		q.createSe()
	}
	if q.Points != nil {
		q.disperse()
	}
}

// helper function for Quadtree.subdivide()
//
// places all points in the tree in the appropriate quadrant,
// and clears the points of this tree.
func (q *Quadtree) disperse() {
	for {
		oldPoints := q.Points
		if oldPoints == nil || oldPoints.Length == 0 {
			break
		}
		newPoints := *oldPoints
		if newPoints.First == nil {
			fmt.Println("nil first with " + strconv.Itoa(oldPoints.Length))
		}
		p := newPoints.First.Point
		newPoints.First = newPoints.First.Next
		newPoints.Length--
//		fmt.Printf("disperse swapping CAS(%p, %p, %p)\n", q.Points, &oldPoints, &newPoints)
		ok := atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Points)), unsafe.Pointer(oldPoints), unsafe.Pointer(&newPoints))
		if !ok {
//			fmt.Println("CAS disperse failed: len(points): " + strconv.Itoa(newPoints.Length))
			continue
		}
//		fmt.Println("CAS disperse succeeded: len(points): " + strconv.Itoa(len(*newPoints)))
//		fmt.Println("CAS disperse succeeded: len(points): " + strconv.Itoa(len(*newPoints)))
//		fmt.Println("disperse inserting")
		ok = q.Nw.Insert(p) || q.Ne.Insert(p) || q.Sw.Insert(p) || q.Se.Insert(p)
		// @todo remove when quadtree is finished. Useful for debugging, but it slows things down
		if !ok {
			panic("quadtree contained a point outside boundary")
		}
	}
	q.Points = nil
}

// helper function for Quadtree.createDir() functions
// creates a quadrant of the current Quadtree, with the given center
func (q *Quadtree) createQuadrant(center Point) *Quadtree {
	return &Quadtree{
		Boundary: &BoundingBox{
			Center: center,
			HalfDimension: Point{q.Boundary.HalfDimension.X / 2.0, q.Boundary.HalfDimension.Y / 2.0},
		},
		Points: NewPointList(q.Points.Capacity),
	}
}

// helper function for Quadtree.subdivide()
// creates the Nw quadrant of the tree
func (q *Quadtree) createNw() {
	center := Point{
		X: q.Boundary.Center.X - q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y - q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Nw)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Ne quadrant of the tree
func (q *Quadtree) createNe() {
	center := Point{
		X: q.Boundary.Center.X + q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y - q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Ne)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Sw quadrant of the tree
func (q *Quadtree) createSw() {
	center := Point{
		X: q.Boundary.Center.X - q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y + q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Sw)), nil, unsafe.Pointer(quadrant))
}

// helper function for Quadtree.subdivide()
// creates the Se quadrant of the tree
func (q *Quadtree) createSe() {
	center := Point{
		X: q.Boundary.Center.X + q.Boundary.HalfDimension.X/2.0,
		Y: q.Boundary.Center.Y + q.Boundary.HalfDimension.Y/2.0,
	}
	quadrant := q.createQuadrant(center)
	// we don't need to check if the CAS fails - if it fails, someone else already created the quadrant
	atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.Se)), nil, unsafe.Pointer(quadrant))
}

func (q *Quadtree) QueryRange(b *BoundingBox) []Point {
	var points []Point
	if !q.Boundary.Intersects(b) {
		return nil
	}
	qPoints := q.Points // this is important. It prevents the loop from segfaulting if a concurrent thread causes a split on this qt
	if qPoints != nil {
		for node := qPoints.First; node != nil; node = node.Next {
			if b.Contains(node.Point) {
				points = append(points, *node.Point)
			}
		}
	}
	if q.Nw != nil {
		points = append(points, q.Nw.QueryRange(b)...)
	}
	if q.Ne != nil {
		points = append(points, q.Ne.QueryRange(b)...)
	}
	if q.Sw != nil {
		points = append(points, q.Sw.QueryRange(b)...)
	}
	if q.Se != nil {
		points = append(points, q.Se.QueryRange(b)...)
	}
	return points
}

func NewQuadTree(b *BoundingBox, capacity int) *Quadtree {
	return &Quadtree {
		Boundary: b,
		Points: NewPointList(capacity),
	}
}

<|endoftext|>


<commit_before>package creds

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

const (
	mfaCodeRegexString = `^\d{6}$`
)

var mfaCodeRegex = regexp.MustCompile(mfaCodeRegexString)

// MfaPrompt defines an object which recieves an Mfa ARN and returns an Mfa code
type MfaPrompt interface {
	Prompt(string) (string, error)
}

// WritableMfaPrompt defines an MFA Prompt which can store a new secret
type WritableMfaPrompt interface {
	Store(string, string) error
	RetryText(string) string
}

// revive:disable-next-line:flag-parameter
func (c Creds) handleMfa(useMfa bool, mfaCode string, mfaPrompt MfaPrompt) (string, string, error) {
	logger.InfoMsg("handling mfa options")

	if !useMfa && mfaCode == "" {
		logger.InfoMsg("mfa is disabled")
		return "", "", nil
	}
	mfaSerial, err := c.MfaArn()
	if err != nil {
		return "", "", err
	}
	if mfaCode != "" {
		logger.InfoMsg("mfa code already provided")
		err := validateMfa(mfaCode)
		if err != nil {
			return "", "", err
		}

		return mfaCode, mfaSerial, nil
	}
	if mfaPrompt == nil {
		logger.InfoMsg("using default mfa prompt")
		// revive:disable-next-line:modifies-parameter
		mfaPrompt = &DefaultMfaPrompt{}
	}
	logger.InfoMsg("prompting for mfa code")
	// revive:disable-next-line:modifies-parameter
	mfaCode, err = mfaPrompt.Prompt(mfaSerial)
	if err != nil {
		return "", "", err
	}
	return mfaCode, mfaSerial, nil
}

// DefaultMfaPrompt defines the standard CLI-based MFA prompt
type DefaultMfaPrompt struct {
	PromptTextFunc func(string) string
}

func defaultPromptTextFunc(_ string) string {
	return "MFA Code: "
}

// Prompt asks the user for their MFA token
func (p *DefaultMfaPrompt) Prompt(arn string) (string, error) {
	mfaReader := bufio.NewReader(os.Stdin)
	pf := PromptTextFunc
	if pf == nil {
		pf = defaultPromptTextFunc
	}
	fmt.Fprint(os.Stderr, pf(arn))
	mfa, err := mfaReader.ReadString('\n')
	if err != nil {
		return "", err
	}
	trimmed := strings.TrimSpace(mfa)
	err = validateMfa(trimmed)
	if err != nil {
		return "", err
	}
	return trimmed, nil
}

func validateMfa(code string) error {
	logger.InfoMsg("validating mfa")
	if mfaCodeRegex.MatchString(code) {
		return nil
	}
	return fmt.Errorf("provided mfa code does not match the necessary format")
}

// MultiMfaPrompt allows a slice of sequential backends to check for Mfa
type MultiMfaPrompt struct {
	Backends []MfaPrompt
}

// Prompt iterates through the backends to find an Mfa code
func (m *MultiMfaPrompt) Prompt(arn string) (string, error) {
	logger.InfoMsgf("looking up %s in multi mfa prompt", arn)
	for index, item := range m.Backends {
		res, err := item.Prompt(arn)
		if err == nil {
			return res, nil
		}
		logger.InfoMsgf("failed backend lookup %d with %s", index, err)
	}
	return "", fmt.Errorf("all backends failed to return mfa code")
}

// Store attempts to store the Mfa in a backend
func (m *MultiMfaPrompt) Store(arn, seed string) error {
	logger.InfoMsgf("attempting to store %s in multi mfa object", arn)
	writer := m.getWriter()
	if writer == nil {
		return fmt.Errorf("no writable mfa backends found")
	}
	return writer.Store(arn, seed)
}

// RetryText returns helper text for retrying a failed mfa storage
func (m *MultiMfaPrompt) RetryText(arn string) string {
	writer := m.getWriter()
	if writer == nil {
		return ""
	}
	return writer.RetryText(arn)
}

func (m *MultiMfaPrompt) getWriter() WritableMfaPrompt {
	for index, item := range m.Backends {
		writer, ok := item.(WritableMfaPrompt)
		if ok {
			logger.InfoMsgf("found mfa writer with index %d", index)
			return writer
		}
	}
	return nil
}
<commit_msg>fix typo<commit_after>package creds

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

const (
	mfaCodeRegexString = `^\d{6}$`
)

var mfaCodeRegex = regexp.MustCompile(mfaCodeRegexString)

// MfaPrompt defines an object which recieves an Mfa ARN and returns an Mfa code
type MfaPrompt interface {
	Prompt(string) (string, error)
}

// WritableMfaPrompt defines an MFA Prompt which can store a new secret
type WritableMfaPrompt interface {
	Store(string, string) error
	RetryText(string) string
}

// revive:disable-next-line:flag-parameter
func (c Creds) handleMfa(useMfa bool, mfaCode string, mfaPrompt MfaPrompt) (string, string, error) {
	logger.InfoMsg("handling mfa options")

	if !useMfa && mfaCode == "" {
		logger.InfoMsg("mfa is disabled")
		return "", "", nil
	}
	mfaSerial, err := c.MfaArn()
	if err != nil {
		return "", "", err
	}
	if mfaCode != "" {
		logger.InfoMsg("mfa code already provided")
		err := validateMfa(mfaCode)
		if err != nil {
			return "", "", err
		}

		return mfaCode, mfaSerial, nil
	}
	if mfaPrompt == nil {
		logger.InfoMsg("using default mfa prompt")
		// revive:disable-next-line:modifies-parameter
		mfaPrompt = &DefaultMfaPrompt{}
	}
	logger.InfoMsg("prompting for mfa code")
	// revive:disable-next-line:modifies-parameter
	mfaCode, err = mfaPrompt.Prompt(mfaSerial)
	if err != nil {
		return "", "", err
	}
	return mfaCode, mfaSerial, nil
}

// DefaultMfaPrompt defines the standard CLI-based MFA prompt
type DefaultMfaPrompt struct {
	PromptTextFunc func(string) string
}

func defaultPromptTextFunc(_ string) string {
	return "MFA Code: "
}

// Prompt asks the user for their MFA token
func (p *DefaultMfaPrompt) Prompt(arn string) (string, error) {
	mfaReader := bufio.NewReader(os.Stdin)
	pf := p.PromptTextFunc
	if pf == nil {
		pf = defaultPromptTextFunc
	}
	fmt.Fprint(os.Stderr, pf(arn))
	mfa, err := mfaReader.ReadString('\n')
	if err != nil {
		return "", err
	}
	trimmed := strings.TrimSpace(mfa)
	err = validateMfa(trimmed)
	if err != nil {
		return "", err
	}
	return trimmed, nil
}

func validateMfa(code string) error {
	logger.InfoMsg("validating mfa")
	if mfaCodeRegex.MatchString(code) {
		return nil
	}
	return fmt.Errorf("provided mfa code does not match the necessary format")
}

// MultiMfaPrompt allows a slice of sequential backends to check for Mfa
type MultiMfaPrompt struct {
	Backends []MfaPrompt
}

// Prompt iterates through the backends to find an Mfa code
func (m *MultiMfaPrompt) Prompt(arn string) (string, error) {
	logger.InfoMsgf("looking up %s in multi mfa prompt", arn)
	for index, item := range m.Backends {
		res, err := item.Prompt(arn)
		if err == nil {
			return res, nil
		}
		logger.InfoMsgf("failed backend lookup %d with %s", index, err)
	}
	return "", fmt.Errorf("all backends failed to return mfa code")
}

// Store attempts to store the Mfa in a backend
func (m *MultiMfaPrompt) Store(arn, seed string) error {
	logger.InfoMsgf("attempting to store %s in multi mfa object", arn)
	writer := m.getWriter()
	if writer == nil {
		return fmt.Errorf("no writable mfa backends found")
	}
	return writer.Store(arn, seed)
}

// RetryText returns helper text for retrying a failed mfa storage
func (m *MultiMfaPrompt) RetryText(arn string) string {
	writer := m.getWriter()
	if writer == nil {
		return ""
	}
	return writer.RetryText(arn)
}

func (m *MultiMfaPrompt) getWriter() WritableMfaPrompt {
	for index, item := range m.Backends {
		writer, ok := item.(WritableMfaPrompt)
		if ok {
			logger.InfoMsgf("found mfa writer with index %d", index)
			return writer
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package grouper_test

import (
	"os"
	"syscall"

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

	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper/0.2"
	"github.com/tedsuo/ifrit/test_helpers"
)

var _ = Describe("Members", func() {
	Describe("ifrit.Envoke()", func() {
		var pinger1 test_helpers.PingChan
		var pinger2 test_helpers.PingChan
		var pGroup ifrit.Process

		BeforeEach(func() {
			pinger1 = make(test_helpers.PingChan)
			pinger2 = make(test_helpers.PingChan)
			pGroup = ifrit.Envoke(grouper.Members{
				{"ping1", pinger1, grouper.RestartMePolicy()},
				{"ping2", pinger2, grouper.StopGroupPolicy()},
			})
		})

		Context("when the linked process is signaled to stop", func() {
			BeforeEach(func() {
				pGroup.Signal(os.Kill)
			})

			It("exits with nil", func() {
				Eventually(pGroup.Wait()).Should(Receive(nil))
			})
		})
	})

	Describe("Signal propogation", func() {
		var recorder1 *test_helpers.SignalRecoder
		var recorder2 *test_helpers.SignalRecoder
		var pGroup ifrit.Process

		BeforeEach(func() {
			recorder1 = test_helpers.NewSignalRecorder(syscall.SIGVTALRM)
			recorder2 = test_helpers.NewSignalRecorder()
			pGroup = ifrit.Envoke(grouper.Members{
				{"recorder1", recorder1, grouper.StopGroupPolicy()},
				{"recorder2", recorder2, grouper.StopGroupPolicy()},
			})

			pGroup.Signal(syscall.SIGVTALRM)
			Eventually(pGroup.Wait()).Should(Receive())
		})

		It("should propogate the initial signal, and the first processes exit signal", func() {
			Ω(recorder2.ReceivedSignals()).Should(Equal([]os.Signal{syscall.SIGVTALRM, os.Interrupt}))
		})
	})
})
<commit_msg>test with all default policies<commit_after>package grouper_test

import (
	"os"
	"syscall"

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

	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper/0.2"
	"github.com/tedsuo/ifrit/test_helpers"
)

var _ = Describe("Members", func() {
	Describe("ifrit.Envoke()", func() {
		var pGroup ifrit.Process

		BeforeEach(func() {
			pinger1 := make(test_helpers.PingChan)
			pinger2 := make(test_helpers.PingChan)
			pinger3 := make(test_helpers.PingChan)
			pinger4 := make(test_helpers.PingChan)
			pGroup = ifrit.Envoke(grouper.Members{
				{"ping1", pinger1, grouper.RestartMePolicy()},
				{"ping2", pinger2, grouper.StopMePolicy()},
				{"ping3", pinger3, grouper.RestartGroupPolicy()},
				{"ping4", pinger4, grouper.StopGroupPolicy()},
			})
		})

		Context("when the linked process is signaled to stop", func() {
			BeforeEach(func() {
				pGroup.Signal(os.Kill)
			})

			It("exits with nil", func() {
				Eventually(pGroup.Wait()).Should(Receive(nil))
			})
		})
	})

	Describe("Signal propogation", func() {
		var recorder1 *test_helpers.SignalRecoder
		var recorder2 *test_helpers.SignalRecoder
		var pGroup ifrit.Process

		BeforeEach(func() {
			recorder1 = test_helpers.NewSignalRecorder(syscall.SIGVTALRM)
			recorder2 = test_helpers.NewSignalRecorder()
			pGroup = ifrit.Envoke(grouper.Members{
				{"recorder1", recorder1, grouper.StopGroupPolicy()},
				{"recorder2", recorder2, grouper.StopGroupPolicy()},
			})

			pGroup.Signal(syscall.SIGVTALRM)
			Eventually(pGroup.Wait()).Should(Receive())
		})

		It("should propogate the initial signal, and the first processes exit signal", func() {
			Ω(recorder2.ReceivedSignals()).Should(Equal([]os.Signal{syscall.SIGVTALRM, os.Interrupt}))
		})
	})
})
<|endoftext|>


<commit_before>// Package exec provides the entry point of the daemon sub-program and helpers
// to spawn a daemon process.
package daemon

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

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

// Daemon keeps configurations for the daemon sub-program. It can be used both
// from the main function for running the daemon and from another process
// (typically the first Elvish shell session) for spawning a daemon.
type Daemon struct {
	// Forked is the number of times the daemon has forked itself.
	Forked int
	// BinPath is the path to the Elvish binary itself, used when forking.
	BinPath string
	// DbPath is the path to the database.
	DbPath string
	// SockPath is the path to the socket on which the daemon will serve
	// requests.
	SockPath string
	// LogPathPrefix is used to derive the name of the log file by adding the
	// pid.
	LogPathPrefix string
}

// Main is the entry point of the daemon sub-program. It takes a serve function
// and returns the exit status. The caller can call os.Exit after doing more
// cleanup work. The exact behavior of this function depends on the -forked
// flag:
//
// When -forked is 0 (which is how the daemon gets started), it forks another
// daemon process, with an empty environment, the working directory changed to
// /, with umask 0077, and in a new process session. All the path flags are
// resolved to absolute paths, and the -fork flag becomes 1.
//
// When -forked is 1, it simply forks another daemon process with all arguments
// passed as is, except -forked which becomes 2.
//
// When -forked is 2, it calls the serve function with the value of -sockpath
// and -dbpath.
//
// These 3 steps implement a standard demonizing procedure. The main deviation
// is that since Go does not support a raw fork call, it has to use fork-exec to
// simulate fork and pass all states via command line flags.
func (d *Daemon) Main(serve func(string, string)) error {
	switch d.Forked {
	case 0:
		var absifyError error
		absify := func(name string, pvalue *string) {
			if absifyError == nil {
				return
			}
			if *pvalue == "" {
				absifyError = fmt.Errorf("flag %s is required for daemon", name)
				return
			}
			newvalue, err := filepath.Abs(*pvalue)
			if err != nil {
				absifyError = fmt.Errorf("cannot convert %s to absolute path: %s", name, err)
			} else {
				*pvalue = newvalue
			}
		}
		absify("-bin", &d.BinPath)
		absify("-db", &d.DbPath)
		absify("-sock", &d.SockPath)
		absify("-logprefix", &d.LogPathPrefix)
		if absifyError != nil {
			return absifyError
		}

		syscall.Umask(0077)
		return d.fork(
			&exec.Cmd{
				Dir:         "/", // cd to /
				Env:         nil, // empty environment
				SysProcAttr: &syscall.SysProcAttr{Setsid: true},
			})
	case 1:
		return d.fork(nil)
	case 2:
		serve(d.SockPath, d.DbPath)
		return nil
	default:
		return fmt.Errorf("-forked is %d, should be 0, 1 or 2", d.Forked)
	}
}

// Spawn spawns a daemon in the background by calling the Elvish binary with
// appropriate flags. If d.BinPath is not set, it attempts to derive the binary
// path first. The fields DbPath, SockPath and LogPathPrefix must be set.
//
// It is supposed to be called from a Elvish shell (as opposed to daemon) process.
func (d *Daemon) Spawn() error {
	binPath := d.BinPath
	// Determine binPath.
	if binPath == "" {
		bin, err := getAbsBinPath()
		if err != nil {
			return errors.New("cannot find elvish: " + err.Error())
		}
		binPath = bin
	}

	return setArgs(
		nil,
		0,
		binPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Run()
}

// getAbsBinPath determines the absolute path to the Elvish binary, first by
// looking at os.Args[0] and then searching for "elvish" in PATH.
func getAbsBinPath() (string, error) {
	if len(os.Args) > 0 {
		arg0 := os.Args[0]
		if path.IsAbs(arg0) {
			return arg0, nil
		} else if strings.Contains(arg0, "/") {
			abs, err := filepath.Abs(arg0)
			if err == nil {
				return abs, nil
			}
			log.Printf("cannot resolve relative arg0 %q, searching in PATH", arg0)
		}
	}
	// Find elvish in PATH
	paths := strings.Split(os.Getenv("PATH"), ":")
	binpath, err := util.Search(paths, "elvish")
	if err != nil {
		return "", err
	}
	return binpath, nil
}

// fork forks a daemon. It is supposed to be called from the daemon.
func (d *Daemon) fork(cmd *exec.Cmd) error {
	err := setArgs(
		cmd,
		d.Forked+1,
		d.BinPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Start()
	return err
}

func setArgs(cmd *exec.Cmd, forkLevel int, binPath, dbPath, sockPath,
	logPathPrefix string) *exec.Cmd {

	if cmd == nil {
		cmd = &exec.Cmd{}
	}

	cmd.Path = binPath
	cmd.Args = []string{
		binPath,
		"-daemon",
		"-forked", strconv.Itoa(forkLevel),
		"-bin", binPath,
		"-db", dbPath,
		"-sock", sockPath,
		"-logprefix", logPathPrefix,
	}

	return cmd
}
<commit_msg>daemon: use os.Executable to get absolute executable path<commit_after>// Package exec provides the entry point of the daemon sub-program and helpers
// to spawn a daemon process.
package daemon

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
)

// Daemon keeps configurations for the daemon sub-program. It can be used both
// from the main function for running the daemon and from another process
// (typically the first Elvish shell session) for spawning a daemon.
type Daemon struct {
	// Forked is the number of times the daemon has forked itself.
	Forked int
	// BinPath is the path to the Elvish binary itself, used when forking.
	BinPath string
	// DbPath is the path to the database.
	DbPath string
	// SockPath is the path to the socket on which the daemon will serve
	// requests.
	SockPath string
	// LogPathPrefix is used to derive the name of the log file by adding the
	// pid.
	LogPathPrefix string
}

// Main is the entry point of the daemon sub-program. It takes a serve function
// and returns the exit status. The caller can call os.Exit after doing more
// cleanup work. The exact behavior of this function depends on the -forked
// flag:
//
// When -forked is 0 (which is how the daemon gets started), it forks another
// daemon process, with an empty environment, the working directory changed to
// /, with umask 0077, and in a new process session. All the path flags are
// resolved to absolute paths, and the -fork flag becomes 1.
//
// When -forked is 1, it simply forks another daemon process with all arguments
// passed as is, except -forked which becomes 2.
//
// When -forked is 2, it calls the serve function with the value of -sockpath
// and -dbpath.
//
// These 3 steps implement a standard demonizing procedure. The main deviation
// is that since Go does not support a raw fork call, it has to use fork-exec to
// simulate fork and pass all states via command line flags.
func (d *Daemon) Main(serve func(string, string)) error {
	switch d.Forked {
	case 0:
		var absifyError error
		absify := func(name string, pvalue *string) {
			if absifyError == nil {
				return
			}
			if *pvalue == "" {
				absifyError = fmt.Errorf("flag %s is required for daemon", name)
				return
			}
			newvalue, err := filepath.Abs(*pvalue)
			if err != nil {
				absifyError = fmt.Errorf("cannot convert %s to absolute path: %s", name, err)
			} else {
				*pvalue = newvalue
			}
		}
		absify("-bin", &d.BinPath)
		absify("-db", &d.DbPath)
		absify("-sock", &d.SockPath)
		absify("-logprefix", &d.LogPathPrefix)
		if absifyError != nil {
			return absifyError
		}

		syscall.Umask(0077)
		return d.fork(
			&exec.Cmd{
				Dir:         "/", // cd to /
				Env:         nil, // empty environment
				SysProcAttr: &syscall.SysProcAttr{Setsid: true},
			})
	case 1:
		return d.fork(nil)
	case 2:
		serve(d.SockPath, d.DbPath)
		return nil
	default:
		return fmt.Errorf("-forked is %d, should be 0, 1 or 2", d.Forked)
	}
}

// Spawn spawns a daemon in the background by calling the Elvish binary with
// appropriate flags. If d.BinPath is not set, it attempts to derive the binary
// path first. The fields DbPath, SockPath and LogPathPrefix must be set.
//
// It is supposed to be called from a Elvish shell (as opposed to daemon) process.
func (d *Daemon) Spawn() error {
	binPath := d.BinPath
	// Determine binPath.
	if binPath == "" {
		bin, err := os.Executable()
		if err != nil {
			return errors.New("cannot find elvish: " + err.Error())
		}
		binPath = bin
	}

	return setArgs(
		nil,
		0,
		binPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Run()
}

// fork forks a daemon. It is supposed to be called from the daemon.
func (d *Daemon) fork(cmd *exec.Cmd) error {
	err := setArgs(
		cmd,
		d.Forked+1,
		d.BinPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Start()
	return err
}

func setArgs(cmd *exec.Cmd, forkLevel int, binPath, dbPath, sockPath,
	logPathPrefix string) *exec.Cmd {

	if cmd == nil {
		cmd = &exec.Cmd{}
	}

	cmd.Path = binPath
	cmd.Args = []string{
		binPath,
		"-daemon",
		"-forked", strconv.Itoa(forkLevel),
		"-bin", binPath,
		"-db", dbPath,
		"-sock", sockPath,
		"-logprefix", logPathPrefix,
	}

	return cmd
}
<|endoftext|>


<commit_before>package main

import (
	"../"
	"flag"
	"fmt"
	"io/ioutil"
)

var (
	file = flag.String("file", "IM-0001-0001.dcm", "the DICOM file you want to parse")
)

func init() {
	flag.Parse()
}

func main() {

	parser, err := dicom.NewParser()
	if err != nil {
		panic(err)
	}

	bytes, err := ioutil.ReadFile(*file)

	if err != nil {
		panic(err)
	}

	data, err := parser.Parse(bytes)

	if err != nil {
		fmt.Println(err)
	}

	elem, _ := data.LookupElement("PatientName")
	name := elem.Value
	fmt.Printf("Patient name: %s\n", name)

}
<commit_msg>Satisfy Wercker<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/gillesdemey/go-dicom"
	"io/ioutil"
)

var (
	file = flag.String("file", "IM-0001-0001.dcm", "the DICOM file you want to parse")
)

func init() {
	flag.Parse()
}

func main() {

	parser, err := dicom.NewParser()
	if err != nil {
		panic(err)
	}

	bytes, err := ioutil.ReadFile(*file)

	if err != nil {
		panic(err)
	}

	data, err := parser.Parse(bytes)

	if err != nil {
		fmt.Println(err)
	}

	elem, _ := data.LookupElement("PatientName")
	name := elem.Value
	fmt.Printf("Patient name: %s\n", name)

}
<|endoftext|>


<commit_before>package ares

import (
	"fmt"
	"log"

	"github.com/nlopes/slack"
)

type Ares struct {
	SlackAppToken string
	SlackBotToken string
	SlackAppID    string
	ImgurClientID string
	BotUserID     string
	// Bot won't be added or re-added to these channels
	ManagedChannels []string
	// list of userIDs of admins
	Admins []string
}

func (a *Ares) initBot() {
	api := slack.New(a.SlackAppToken)
	a.getBotandAdmin()

	channels, err := api.GetChannels(true)

	if err != nil {
		log.Fatal("Failed to get public channels list: ", err.Error())
	}

	for _, channel := range channels {
		a.addBotChannel(channel.ID)
	}

	groups, err := api.GetGroups(true)

	if err != nil {
		log.Fatal("Failed to get private channels list: ", err.Error())
	}

	for _, group := range groups {
		a.addBotGroup(group.ID)
	}
}

// Fetches the bot user ID and also saves the admin user ids
func (a *Ares) getBotandAdmin() {
	api := slack.New(a.SlackAppToken)
	users, err := api.GetUsers()

	if err != nil {
		log.Fatal("Failed to fetch slack users info", err.Error())
	}

	for _, user := range users {

		if user.Profile.ApiAppID == a.SlackAppID {
			a.BotUserID = user.ID
		}

		if user.IsAdmin {
			a.Admins = append(a.Admins, user.ID)
		}
	}

	if a.BotUserID == "" {
		log.Fatal("Unable to find bot user on the Slack")
	}
}

func (a *Ares) deleteFile(fileId string) {
	api := slack.New(a.SlackAppToken)

	if err := api.DeleteFile(fileId); err != nil {
		fmt.Println("Deletion of the file failed:", err.Error())
	}
}

func (a *Ares) addBotChannel(channelID string) {
	api := slack.New(a.SlackAppToken)
	if _, err := api.InviteUserToChannel(channelID, a.BotUserID); err != nil {
		if err.Error() != "already_in_channel" {
			log.Println(fmt.Sprintf("Failed to add bot to %s: %s", channelID, err.Error()))
		}
	}
}

func (a *Ares) addBotGroup(group string) {
	api := slack.New(a.SlackAppToken)
	if _, _, err := api.InviteUserToGroup(group, a.BotUserID); err != nil {
		log.Println(fmt.Sprintf("Failed to add bot to %s: %s", group, err.Error()))
	}
}

func (a *Ares) notifyUser(user, deleteHash string) {
	api := slack.New(a.SlackBotToken)
	params := slack.PostMessageParameters{}
	attachment := slack.Attachment{
		Color:   "danger",
		Pretext: "Hey there, image uploads to this slack is disabled. Please use external image upload services next time. This incident has been reported :squirrel:",
		Text:    "I have uploaded the image to Imgur for now.",
		Fields: []slack.AttachmentField{
			{
				Title: "If you wish to delete the image uploaded, click on the following link",
				Value: fmt.Sprintf("https://imgur.com/delete/%s", deleteHash),
			},
		},
	}
	params.Attachments = []slack.Attachment{attachment}
	_, _, err := api.PostMessage(user, "", params)
	if err != nil {
		log.Println("Failed to send DM to user", err.Error())
	}
}

func (a *Ares) sendImgToSlack(channel, user, url, commentText string) {
	api := slack.New(a.SlackBotToken)
	params := slack.PostMessageParameters{}
	attachment := slack.Attachment{
		Color:    "#D3D3D3",
		Text:     fmt.Sprintf("image originally uploaded by <@%s>", user),
		ImageURL: url,
		Fields: []slack.AttachmentField{
			{
				Title: "",
				Value: commentText,
			},
		},
	}
	params.Attachments = []slack.Attachment{attachment}
	_, _, err := api.PostMessage(channel, "", params)
	if err != nil {
		log.Println("Failed to send DM to user", err.Error())
	}
}

func (a *Ares) handleFile(file *slack.File, channel string) {
	commentText := file.InitialComment.Comment

	resp := uploadToImgur(file.URLPrivateDownload, a.SlackAppToken, a.ImgurClientID)
	if resp.Status != 200 {
		log.Println("Failed to download/upload")
		return
	}

	a.notifyUser(file.User, resp.Data.Deletehash)
	a.sendImgToSlack(channel, file.User, resp.Data.Link, commentText)
	a.deleteFile(file.ID)
}

func isImageFile(fileType string) bool {
	for _, fType := range []string{"jpg", "jpeg", "png", "gif"} {
		if fType == fileType {
			return true
		}
	}
	return false
}

func (a *Ares) Run() {
	api := slack.New(a.SlackBotToken)

	a.initBot()
	log.Println("Bot initialized. Starting moderation duty.")

	rtm := api.NewRTM()
	go rtm.ManageConnection()

	for msg := range rtm.IncomingEvents {
		switch ev := msg.Data.(type) {

		case *slack.MessageEvent:
			if ev.SubType == "file_share" {
				if isImageFile(ev.File.Filetype) {
					a.handleFile(ev.File, ev.Channel)
				}
			}

		case *slack.TeamJoinEvent:
			if !ev.User.IsBot {
				a.onBoardUser(ev.User)
			}

		case *slack.GroupLeftEvent:
			log.Println("Bot was removed from private channel: ", ev.Channel)
			a.addBotGroup(ev.Channel)

		case *slack.ChannelLeftEvent:
			log.Println("Bot was removed from public channel: ", ev.Channel)
			a.addBotChannel(ev.Channel)

		case *slack.RTMError:
			fmt.Printf("Error: %s\n", ev.Error())

		case *slack.InvalidAuthEvent:
			fmt.Printf("Invalid credentials")
			return

		default:
			// Ignore other events..
			// fmt.Printf("Unexpected: %v\n", msg.Data)
		}
	}
}
<commit_msg>Delete messages of the user if he is muted<commit_after>package ares

import (
	"fmt"
	"log"

	"github.com/nlopes/slack"
)

type Ares struct {
	SlackAppToken string
	SlackBotToken string
	SlackAppID    string
	ImgurClientID string
	BotUserID     string
	// Bot won't be added or re-added to these channels
	ManagedChannels []string
	// list of userIDs of admins
	Admins []string
	// maintain a user dict
	Users map[string]string
	// Muted users list
	MutedUsers map[string]bool
}

func (a *Ares) initBot() {
	api := slack.New(a.SlackAppToken)
	a.getBotandAdmin()

	channels, err := api.GetChannels(true)

	if err != nil {
		log.Fatal("Failed to get public channels list: ", err.Error())
	}

	for _, channel := range channels {
		a.addBotChannel(channel.ID)
	}

	groups, err := api.GetGroups(true)

	if err != nil {
		log.Fatal("Failed to get private channels list: ", err.Error())
	}

	for _, group := range groups {
		a.addBotGroup(group.ID)
	}
}

// Fetches the bot user ID and also saves the admin user ids
func (a *Ares) getBotandAdmin() {
	api := slack.New(a.SlackAppToken)
	users, err := api.GetUsers()

	if err != nil {
		log.Fatal("Failed to fetch slack users info", err.Error())
	}

	a.Users = make(map[string]string)
	a.MutedUsers = make(map[string]bool)

	for _, user := range users {

		if user.Profile.ApiAppID == a.SlackAppID {
			a.BotUserID = user.ID
		}

		if user.IsAdmin {
			a.Admins = append(a.Admins, user.ID)
		} else {
			a.Users[user.Name] = user.ID
		}
	}

	if a.BotUserID == "" {
		log.Fatal("Unable to find bot user on the Slack")
	}
}

func (a *Ares) deleteMsg(channel, msgTimestamp string) {
	api := slack.New(a.SlackAppToken)

	if _, _, err := api.DeleteMessage(channel, msgTimestamp); err != nil {
		log.Println("Failed to delete msg: ", err.Error())
	}
}

func (a *Ares) deleteFile(fileId string) {
	api := slack.New(a.SlackAppToken)

	if err := api.DeleteFile(fileId); err != nil {
		fmt.Println("Deletion of the file failed:", err.Error())
	}
}

func (a *Ares) addBotChannel(channelID string) {
	api := slack.New(a.SlackAppToken)
	if _, err := api.InviteUserToChannel(channelID, a.BotUserID); err != nil {
		if err.Error() != "already_in_channel" {
			log.Println(fmt.Sprintf("Failed to add bot to %s: %s", channelID, err.Error()))
		}
	}
}

func (a *Ares) addBotGroup(group string) {
	api := slack.New(a.SlackAppToken)
	if _, _, err := api.InviteUserToGroup(group, a.BotUserID); err != nil {
		log.Println(fmt.Sprintf("Failed to add bot to %s: %s", group, err.Error()))
	}
}

func (a *Ares) notifyUser(user, deleteHash string) {
	api := slack.New(a.SlackBotToken)
	params := slack.PostMessageParameters{}
	attachment := slack.Attachment{
		Color:   "danger",
		Pretext: "Hey there, image uploads to this slack is disabled. Please use external image upload services next time. This incident has been reported :squirrel:",
		Text:    "I have uploaded the image to Imgur for now.",
		Fields: []slack.AttachmentField{
			{
				Title: "If you wish to delete the image uploaded, click on the following link",
				Value: fmt.Sprintf("https://imgur.com/delete/%s", deleteHash),
			},
		},
	}
	params.Attachments = []slack.Attachment{attachment}
	_, _, err := api.PostMessage(user, "", params)
	if err != nil {
		log.Println("Failed to send DM to user", err.Error())
	}
}

func (a *Ares) sendImgToSlack(channel, user, url, commentText string) {
	api := slack.New(a.SlackBotToken)
	params := slack.PostMessageParameters{}
	attachment := slack.Attachment{
		Color:    "#D3D3D3",
		Text:     fmt.Sprintf("image originally uploaded by <@%s>", user),
		ImageURL: url,
		Fields: []slack.AttachmentField{
			{
				Title: "",
				Value: commentText,
			},
		},
	}
	params.Attachments = []slack.Attachment{attachment}
	_, _, err := api.PostMessage(channel, "", params)
	if err != nil {
		log.Println("Failed to send DM to user", err.Error())
	}
}

func (a *Ares) handleFile(file *slack.File, channel string) {
	commentText := file.InitialComment.Comment

	resp := uploadToImgur(file.URLPrivateDownload, a.SlackAppToken, a.ImgurClientID)
	if resp.Status != 200 {
		log.Println("Failed to download/upload")
		return
	}

	a.notifyUser(file.User, resp.Data.Deletehash)
	a.sendImgToSlack(channel, file.User, resp.Data.Link, commentText)
	a.deleteFile(file.ID)
}

func isImageFile(fileType string) bool {
	for _, fType := range []string{"jpg", "jpeg", "png", "gif"} {
		if fType == fileType {
			return true
		}
	}
	return false
}

func (a *Ares) Run() {
	api := slack.New(a.SlackBotToken)

	a.initBot()
	log.Println("Bot initialized. Starting moderation duty.")

	rtm := api.NewRTM()
	go rtm.ManageConnection()

	for msg := range rtm.IncomingEvents {
		switch ev := msg.Data.(type) {

		case *slack.MessageEvent:

			if a.isAdminUser(ev.Msg.User) {
				a.performMuteAction(ev.Msg.Text)
			} else if a.isMuted(ev.Msg.User) {
				go a.deleteMsg(ev.Msg.Channel, ev.Msg.Timestamp)
			}

			if ev.SubType == "file_share" {
				if isImageFile(ev.File.Filetype) {
					a.handleFile(ev.File, ev.Channel)
				}
			}

		case *slack.TeamJoinEvent:
			if !ev.User.IsBot {
				a.onBoardUser(ev.User)
			}

		case *slack.GroupLeftEvent:
			log.Println("Bot was removed from private channel: ", ev.Channel)
			a.addBotGroup(ev.Channel)

		case *slack.ChannelLeftEvent:
			log.Println("Bot was removed from public channel: ", ev.Channel)
			a.addBotChannel(ev.Channel)

		case *slack.RTMError:
			fmt.Printf("Error: %s\n", ev.Error())

		case *slack.InvalidAuthEvent:
			fmt.Printf("Invalid credentials")
			return

		default:
			// Ignore other events..
			// fmt.Printf("Unexpected: %v\n", msg.Data)
		}
	}
}
<|endoftext|>


<commit_before>// Package core handles the main operations of the Red October server.
//
// Copyright (c) 2013 CloudFlare, Inc.

package core

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"

	"github.com/cloudflare/redoctober/cryptor"
	"github.com/cloudflare/redoctober/keycache"
	"github.com/cloudflare/redoctober/passvault"
)

var (
	crypt   cryptor.Cryptor
	records passvault.Records
	cache   keycache.Cache
)

// Each of these structures corresponds to the JSON expected on the
// correspondingly named URI (e.g. the delegate structure maps to the
// JSON that should be sent on the /delegate URI and it is handled by
// the Delegate function below).

type CreateRequest struct {
	Name     string
	Password string
}

type SummaryRequest struct {
	Name     string
	Password string
}

type DelegateRequest struct {
	Name     string
	Password string

	Uses   int
	Time   string
	Users  []string
	Labels []string
}

type PasswordRequest struct {
	Name     string
	Password string

	NewPassword string
}

type EncryptRequest struct {
	Name     string
	Password string

	Owners      []string
	LeftOwners  []string
	RightOwners []string

	Data []byte

	Labels []string
}

type DecryptRequest struct {
	Name     string
	Password string

	Data []byte
}

type OwnersRequest struct {
	Data []byte
}

type ModifyRequest struct {
	Name     string
	Password string

	ToModify string
	Command  string
}

type ExportRequest struct {
	Name     string
	Password string
}

// These structures map the JSON responses that will be sent from the API

type ResponseData struct {
	Status   string
	Response []byte `json:",omitempty"`
}

type SummaryData struct {
	Status string
	Live   map[string]keycache.ActiveUser
	All    map[string]passvault.Summary
}

type DecryptWithDelegates struct {
	Data      []byte
	Secure    bool
	Delegates []string
}

type OwnersData struct {
	Status string
	Owners []string
}

// Helper functions that create JSON responses sent by core

func jsonStatusOk() ([]byte, error) {
	return json.Marshal(ResponseData{Status: "ok"})
}
func jsonStatusError(err error) ([]byte, error) {
	return json.Marshal(ResponseData{Status: err.Error()})
}
func jsonSummary() ([]byte, error) {
	return json.Marshal(SummaryData{Status: "ok", Live: cache.GetSummary(), All: records.GetSummary()})
}
func jsonResponse(resp []byte) ([]byte, error) {
	return json.Marshal(ResponseData{Status: "ok", Response: resp})
}

// validateUser checks that the username and password passed in are
// correct. If admin is true, the user must be an admin as well.
func validateUser(name, password string, admin bool) error {
	if records.NumRecords() == 0 {
		return errors.New("Vault is not created yet")
	}

	pr, ok := records.GetRecord(name)
	if !ok {
		return errors.New("User not present")
	}

	if err := pr.ValidatePassword(password); err != nil {
		return err
	}

	if admin && !pr.IsAdmin() {
		return errors.New("Admin required")
	}

	return nil
}

// validateName checks that the username and password pass the minimal
// validation check
func validateName(name, password string) error {
	if name == "" {
		return errors.New("User name must not be blank")
	}
	if password == "" {
		return errors.New("Password must be at least one character")
	}

	return nil
}

// Init reads the records from disk from a given path
func Init(path string) error {
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.init failed: %v", err)
		} else {
			log.Printf("core.init success: path=%s", path)
		}
	}()

	if records, err = passvault.InitFrom(path); err != nil {
		err = fmt.Errorf("failed to load password vault %s: %s", path, err)
	}

	cache = keycache.Cache{UserKeys: make(map[string]keycache.ActiveUser)}
	crypt = cryptor.New(&records, &cache)

	return err
}

// Create processes a create request.
func Create(jsonIn []byte) ([]byte, error) {
	var s CreateRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.create failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.create success: user=%s", s.Name)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() != 0 {
		err = errors.New("Vault is already created")
		return jsonStatusError(err)
	}

	// Validate the Name and Password as valid
	if err = validateName(s.Name, s.Password); err != nil {
		return jsonStatusError(err)
	}

	if _, err = records.AddNewRecord(s.Name, s.Password, true, passvault.DefaultRecordType); err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Summary processes a summary request.
func Summary(jsonIn []byte) ([]byte, error) {
	var s SummaryRequest
	var err error
	cache.Refresh()

	defer func() {
		if err != nil {
			log.Printf("core.summary failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.summary success: user=%s", s.Name)
		}
	}()

	if err := json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		err = errors.New("vault has not been created")
		return jsonStatusError(err)
	}

	if err := validateUser(s.Name, s.Password, false); err != nil {
		return jsonStatusError(err)
	}

	return jsonSummary()
}

// Delegate processes a delegation request.
func Delegate(jsonIn []byte) ([]byte, error) {
	var s DelegateRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.delegate failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.delegate success: user=%s uses=%d time=%s users=%v labels=%v", s.Name, s.Uses, s.Time, s.Users, s.Labels)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		errors.New("Vault is not created yet")
		return jsonStatusError(err)
	}

	// Validate the Name and Password as valid
	if err = validateName(s.Name, s.Password); err != nil {
		return jsonStatusError(err)
	}

	// Find password record for user and verify that their password
	// matches. If not found then add a new entry for this user.

	pr, found := records.GetRecord(s.Name)
	if found {
		if err = pr.ValidatePassword(s.Password); err != nil {
			return jsonStatusError(err)
		}
	} else {
		if pr, err = records.AddNewRecord(s.Name, s.Password, false, passvault.DefaultRecordType); err != nil {
			return jsonStatusError(err)
		}
	}

	// add signed-in record to active set
	if err = cache.AddKeyFromRecord(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Time); err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Password processes a password change request.
func Password(jsonIn []byte) ([]byte, error) {
	var err error
	var s PasswordRequest

	defer func() {
		if err != nil {
			log.Printf("core.password failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.password success: user=%s", s.Name)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		err = errors.New("Vault is not created yet")
		return jsonStatusError(err)
	}

	// add signed-in record to active set
	err = records.ChangePassword(s.Name, s.Password, s.NewPassword)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Encrypt processes an encrypt request.
func Encrypt(jsonIn []byte) ([]byte, error) {
	var s EncryptRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.encrypt failed: user=%s size=%d %v", s.Name, len(s.Data), err)
		} else {
			log.Printf("core.encrypt success: user=%s size=%d", s.Name, len(s.Data))
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	if err = validateUser(s.Name, s.Password, false); err != nil {
		return jsonStatusError(err)
	}

	access := cryptor.AccessStructure{
		Names:      s.Owners,
		LeftNames:  s.LeftOwners,
		RightNames: s.RightOwners,
	}

	resp, err := crypt.Encrypt(s.Data, s.Labels, access)
	if err != nil {
		return jsonStatusError(err)
	}
	return jsonResponse(resp)
}

// Decrypt processes a decrypt request.
func Decrypt(jsonIn []byte) ([]byte, error) {
	var s DecryptRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.decrypt failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.decrypt success: user=%s", s.Name)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	err = validateUser(s.Name, s.Password, false)
	if err != nil {
		return jsonStatusError(err)
	}

	data, names, secure, err := crypt.Decrypt(s.Data, s.Name)
	if err != nil {
		return jsonStatusError(err)
	}

	resp := &DecryptWithDelegates{
		Data:      data,
		Secure:    secure,
		Delegates: names,
	}

	out, err := json.Marshal(resp)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonResponse(out)
}

// Modify processes a modify request.
func Modify(jsonIn []byte) ([]byte, error) {
	var s ModifyRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.modify failed: user=%s target=%s command=%s %v", s.Name, s.ToModify, s.Command, err)
		} else {
			log.Printf("core.modify success: user=%s target=%s command=%s", s.Name, s.ToModify, s.Command)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	if err = validateUser(s.Name, s.Password, true); err != nil {
		return jsonStatusError(err)
	}

	if _, ok := records.GetRecord(s.ToModify); !ok {
		err = errors.New("core: record to modify missing")
		return jsonStatusError(err)
	}

	if s.Name == s.ToModify {
		err = errors.New("core: cannot modify own record")
		return jsonStatusError(err)
	}

	switch s.Command {
	case "delete":
		err = records.DeleteRecord(s.ToModify)
	case "revoke":
		err = records.RevokeRecord(s.ToModify)
	case "admin":
		err = records.MakeAdmin(s.ToModify)
	default:
		err = fmt.Errorf("core: unknown command '%s' passed to modify", s.Command)
		return jsonStatusError(err)
	}

	if err != nil {
		return jsonStatusError(err)
	} else {
		return jsonStatusOk()
	}
}

// Owners processes a owners request.
func Owners(jsonIn []byte) ([]byte, error) {
	var s OwnersRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.owners failed: size=%d %v", len(s.Data), err)
		} else {
			log.Printf("core.owners success: size=%d", len(s.Data))
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	names, err := crypt.GetOwners(s.Data)
	if err != nil {
		return jsonStatusError(err)
	}

	return json.Marshal(OwnersData{Status: "ok", Owners: names})
}

// Export returns a backed up vault.
func Export(jsonIn []byte) ([]byte, error) {
	var s ExportRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.export failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.export success: user=%s", s.Name)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	err = validateUser(s.Name, s.Password, true)
	if err != nil {
		return jsonStatusError(err)
	}

	out, err := json.Marshal(records)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonResponse(out)
}
<commit_msg>Don't discard result of errors.New.<commit_after>// Package core handles the main operations of the Red October server.
//
// Copyright (c) 2013 CloudFlare, Inc.

package core

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"

	"github.com/cloudflare/redoctober/cryptor"
	"github.com/cloudflare/redoctober/keycache"
	"github.com/cloudflare/redoctober/passvault"
)

var (
	crypt   cryptor.Cryptor
	records passvault.Records
	cache   keycache.Cache
)

// Each of these structures corresponds to the JSON expected on the
// correspondingly named URI (e.g. the delegate structure maps to the
// JSON that should be sent on the /delegate URI and it is handled by
// the Delegate function below).

type CreateRequest struct {
	Name     string
	Password string
}

type SummaryRequest struct {
	Name     string
	Password string
}

type DelegateRequest struct {
	Name     string
	Password string

	Uses   int
	Time   string
	Users  []string
	Labels []string
}

type PasswordRequest struct {
	Name     string
	Password string

	NewPassword string
}

type EncryptRequest struct {
	Name     string
	Password string

	Owners      []string
	LeftOwners  []string
	RightOwners []string

	Data []byte

	Labels []string
}

type DecryptRequest struct {
	Name     string
	Password string

	Data []byte
}

type OwnersRequest struct {
	Data []byte
}

type ModifyRequest struct {
	Name     string
	Password string

	ToModify string
	Command  string
}

type ExportRequest struct {
	Name     string
	Password string
}

// These structures map the JSON responses that will be sent from the API

type ResponseData struct {
	Status   string
	Response []byte `json:",omitempty"`
}

type SummaryData struct {
	Status string
	Live   map[string]keycache.ActiveUser
	All    map[string]passvault.Summary
}

type DecryptWithDelegates struct {
	Data      []byte
	Secure    bool
	Delegates []string
}

type OwnersData struct {
	Status string
	Owners []string
}

// Helper functions that create JSON responses sent by core

func jsonStatusOk() ([]byte, error) {
	return json.Marshal(ResponseData{Status: "ok"})
}
func jsonStatusError(err error) ([]byte, error) {
	return json.Marshal(ResponseData{Status: err.Error()})
}
func jsonSummary() ([]byte, error) {
	return json.Marshal(SummaryData{Status: "ok", Live: cache.GetSummary(), All: records.GetSummary()})
}
func jsonResponse(resp []byte) ([]byte, error) {
	return json.Marshal(ResponseData{Status: "ok", Response: resp})
}

// validateUser checks that the username and password passed in are
// correct. If admin is true, the user must be an admin as well.
func validateUser(name, password string, admin bool) error {
	if records.NumRecords() == 0 {
		return errors.New("Vault is not created yet")
	}

	pr, ok := records.GetRecord(name)
	if !ok {
		return errors.New("User not present")
	}

	if err := pr.ValidatePassword(password); err != nil {
		return err
	}

	if admin && !pr.IsAdmin() {
		return errors.New("Admin required")
	}

	return nil
}

// validateName checks that the username and password pass the minimal
// validation check
func validateName(name, password string) error {
	if name == "" {
		return errors.New("User name must not be blank")
	}
	if password == "" {
		return errors.New("Password must be at least one character")
	}

	return nil
}

// Init reads the records from disk from a given path
func Init(path string) error {
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.init failed: %v", err)
		} else {
			log.Printf("core.init success: path=%s", path)
		}
	}()

	if records, err = passvault.InitFrom(path); err != nil {
		err = fmt.Errorf("failed to load password vault %s: %s", path, err)
	}

	cache = keycache.Cache{UserKeys: make(map[string]keycache.ActiveUser)}
	crypt = cryptor.New(&records, &cache)

	return err
}

// Create processes a create request.
func Create(jsonIn []byte) ([]byte, error) {
	var s CreateRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.create failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.create success: user=%s", s.Name)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() != 0 {
		err = errors.New("Vault is already created")
		return jsonStatusError(err)
	}

	// Validate the Name and Password as valid
	if err = validateName(s.Name, s.Password); err != nil {
		return jsonStatusError(err)
	}

	if _, err = records.AddNewRecord(s.Name, s.Password, true, passvault.DefaultRecordType); err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Summary processes a summary request.
func Summary(jsonIn []byte) ([]byte, error) {
	var s SummaryRequest
	var err error
	cache.Refresh()

	defer func() {
		if err != nil {
			log.Printf("core.summary failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.summary success: user=%s", s.Name)
		}
	}()

	if err := json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		err = errors.New("vault has not been created")
		return jsonStatusError(err)
	}

	if err := validateUser(s.Name, s.Password, false); err != nil {
		return jsonStatusError(err)
	}

	return jsonSummary()
}

// Delegate processes a delegation request.
func Delegate(jsonIn []byte) ([]byte, error) {
	var s DelegateRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.delegate failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.delegate success: user=%s uses=%d time=%s users=%v labels=%v", s.Name, s.Uses, s.Time, s.Users, s.Labels)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		err = errors.New("Vault is not created yet")
		return jsonStatusError(err)
	}

	// Validate the Name and Password as valid
	if err = validateName(s.Name, s.Password); err != nil {
		return jsonStatusError(err)
	}

	// Find password record for user and verify that their password
	// matches. If not found then add a new entry for this user.

	pr, found := records.GetRecord(s.Name)
	if found {
		if err = pr.ValidatePassword(s.Password); err != nil {
			return jsonStatusError(err)
		}
	} else {
		if pr, err = records.AddNewRecord(s.Name, s.Password, false, passvault.DefaultRecordType); err != nil {
			return jsonStatusError(err)
		}
	}

	// add signed-in record to active set
	if err = cache.AddKeyFromRecord(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Time); err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Password processes a password change request.
func Password(jsonIn []byte) ([]byte, error) {
	var err error
	var s PasswordRequest

	defer func() {
		if err != nil {
			log.Printf("core.password failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.password success: user=%s", s.Name)
		}
	}()

	if err = json.Unmarshal(jsonIn, &s); err != nil {
		return jsonStatusError(err)
	}

	if records.NumRecords() == 0 {
		err = errors.New("Vault is not created yet")
		return jsonStatusError(err)
	}

	// add signed-in record to active set
	err = records.ChangePassword(s.Name, s.Password, s.NewPassword)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonStatusOk()
}

// Encrypt processes an encrypt request.
func Encrypt(jsonIn []byte) ([]byte, error) {
	var s EncryptRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.encrypt failed: user=%s size=%d %v", s.Name, len(s.Data), err)
		} else {
			log.Printf("core.encrypt success: user=%s size=%d", s.Name, len(s.Data))
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	if err = validateUser(s.Name, s.Password, false); err != nil {
		return jsonStatusError(err)
	}

	access := cryptor.AccessStructure{
		Names:      s.Owners,
		LeftNames:  s.LeftOwners,
		RightNames: s.RightOwners,
	}

	resp, err := crypt.Encrypt(s.Data, s.Labels, access)
	if err != nil {
		return jsonStatusError(err)
	}
	return jsonResponse(resp)
}

// Decrypt processes a decrypt request.
func Decrypt(jsonIn []byte) ([]byte, error) {
	var s DecryptRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.decrypt failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.decrypt success: user=%s", s.Name)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	err = validateUser(s.Name, s.Password, false)
	if err != nil {
		return jsonStatusError(err)
	}

	data, names, secure, err := crypt.Decrypt(s.Data, s.Name)
	if err != nil {
		return jsonStatusError(err)
	}

	resp := &DecryptWithDelegates{
		Data:      data,
		Secure:    secure,
		Delegates: names,
	}

	out, err := json.Marshal(resp)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonResponse(out)
}

// Modify processes a modify request.
func Modify(jsonIn []byte) ([]byte, error) {
	var s ModifyRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.modify failed: user=%s target=%s command=%s %v", s.Name, s.ToModify, s.Command, err)
		} else {
			log.Printf("core.modify success: user=%s target=%s command=%s", s.Name, s.ToModify, s.Command)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	if err = validateUser(s.Name, s.Password, true); err != nil {
		return jsonStatusError(err)
	}

	if _, ok := records.GetRecord(s.ToModify); !ok {
		err = errors.New("core: record to modify missing")
		return jsonStatusError(err)
	}

	if s.Name == s.ToModify {
		err = errors.New("core: cannot modify own record")
		return jsonStatusError(err)
	}

	switch s.Command {
	case "delete":
		err = records.DeleteRecord(s.ToModify)
	case "revoke":
		err = records.RevokeRecord(s.ToModify)
	case "admin":
		err = records.MakeAdmin(s.ToModify)
	default:
		err = fmt.Errorf("core: unknown command '%s' passed to modify", s.Command)
		return jsonStatusError(err)
	}

	if err != nil {
		return jsonStatusError(err)
	} else {
		return jsonStatusOk()
	}
}

// Owners processes a owners request.
func Owners(jsonIn []byte) ([]byte, error) {
	var s OwnersRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.owners failed: size=%d %v", len(s.Data), err)
		} else {
			log.Printf("core.owners success: size=%d", len(s.Data))
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	names, err := crypt.GetOwners(s.Data)
	if err != nil {
		return jsonStatusError(err)
	}

	return json.Marshal(OwnersData{Status: "ok", Owners: names})
}

// Export returns a backed up vault.
func Export(jsonIn []byte) ([]byte, error) {
	var s ExportRequest
	var err error

	defer func() {
		if err != nil {
			log.Printf("core.export failed: user=%s %v", s.Name, err)
		} else {
			log.Printf("core.export success: user=%s", s.Name)
		}
	}()

	err = json.Unmarshal(jsonIn, &s)
	if err != nil {
		return jsonStatusError(err)
	}

	err = validateUser(s.Name, s.Password, true)
	if err != nil {
		return jsonStatusError(err)
	}

	out, err := json.Marshal(records)
	if err != nil {
		return jsonStatusError(err)
	}

	return jsonResponse(out)
}
<|endoftext|>


<commit_before>/*
                       WARNING WARNING WARNING

                Attention all potential contributors

   This testfile is not in the best state. We've been slowly transitioning
   from the built in "testing" package to using Ginkgo. As you can see, we've
   changed the format, but a lot of the setup, test body, descriptions, etc
   are either hardcoded, completely lacking, or misleading.

   For example:

   Describe("Testing with ginkgo"...)      // This is not a great description
   It("TestDoesSoemthing"...)              // This is a horrible description

   Describe("create-user command"...       // Describe the actual object under test
   It("creates a user when provided ..."   // this is more descriptive

   For good examples of writing Ginkgo tests for the cli, refer to

   src/github.com/cloudfoundry/cli/cf/commands/application/delete_app_test.go
   src/github.com/cloudfoundry/cli/cf/terminal/ui_test.go
   src/github.com/cloudfoundry/loggregator_consumer/consumer_test.go
*/

package api_test

import (
	"github.com/cloudfoundry/cli/cf/net"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testnet "github.com/cloudfoundry/cli/testhelpers/net"
	"net/http"
	"net/http/httptest"
	"time"

	. "github.com/cloudfoundry/cli/cf/api"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("AppSummaryRepository", func() {
	It("TestGetAppSummariesInCurrentSpace", func() {
		getAppSummariesRequest := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
			Method:   "GET",
			Path:     "/v2/spaces/my-space-guid/summary",
			Response: testnet.TestResponse{Status: http.StatusOK, Body: getAppSummariesResponseBody},
		})

		ts, handler, repo := createAppSummaryRepo([]testnet.TestRequest{getAppSummariesRequest})
		defer ts.Close()

		apps, apiErr := repo.GetSummariesInCurrentSpace()
		Expect(handler).To(testnet.HaveAllRequestsCalled())

		Expect(apiErr).NotTo(HaveOccurred())
		Expect(2).To(Equal(len(apps)))

		app1 := apps[0]
		Expect(app1.Name).To(Equal("app1"))
		Expect(app1.Guid).To(Equal("app-1-guid"))
		Expect(len(app1.Routes)).To(Equal(1))
		Expect(app1.Routes[0].URL()).To(Equal("app1.cfapps.io"))

		Expect(app1.State).To(Equal("started"))
		Expect(app1.InstanceCount).To(Equal(1))
		Expect(app1.RunningInstances).To(Equal(1))
		Expect(app1.Memory).To(Equal(uint64(128)))

		app2 := apps[1]
		Expect(app2.Name).To(Equal("app2"))
		Expect(app2.Guid).To(Equal("app-2-guid"))
		Expect(len(app2.Routes)).To(Equal(2))
		Expect(app2.Routes[0].URL()).To(Equal("app2.cfapps.io"))
		Expect(app2.Routes[1].URL()).To(Equal("foo.cfapps.io"))

		Expect(app2.State).To(Equal("started"))
		Expect(app2.InstanceCount).To(Equal(3))
		Expect(app2.RunningInstances).To(Equal(1))
		Expect(app2.Memory).To(Equal(uint64(512)))
	})
})

var getAppSummariesResponseBody = `
{
  "apps":[
    {
      "guid":"app-1-guid",
      "routes":[
        {
          "guid":"route-1-guid",
          "host":"app1",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        }
      ],
      "running_instances":1,
      "name":"app1",
      "memory":128,
      "instances":1,
      "state":"STARTED",
      "service_names":[
      	"my-service-instance"
      ]
    },{
      "guid":"app-2-guid",
      "routes":[
        {
          "guid":"route-2-guid",
          "host":"app2",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        },
        {
          "guid":"route-2-guid",
          "host":"foo",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        }
      ],
      "running_instances":1,
      "name":"app2",
      "memory":512,
      "instances":3,
      "state":"STARTED",
      "service_names":[
      	"my-service-instance"
      ]
    }
  ]
}`

func createAppSummaryRepo(requests []testnet.TestRequest) (ts *httptest.Server, handler *testnet.TestHandler, repo AppSummaryRepository) {
	ts, handler = testnet.NewServer(requests)
	configRepo := testconfig.NewRepositoryWithDefaults()
	configRepo.SetApiEndpoint(ts.URL)
	gateway := net.NewCloudControllerGateway(configRepo, time.Now)
	repo = NewCloudControllerAppSummaryRepository(configRepo, gateway)
	return
}
<commit_msg>Cleanup app_summary repo tests<commit_after>package api_test

import (
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cloudfoundry/cli/cf/net"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testnet "github.com/cloudfoundry/cli/testhelpers/net"

	. "github.com/cloudfoundry/cli/cf/api"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("AppSummaryRepository", func() {
	var (
		testServer *httptest.Server
		handler    *testnet.TestHandler
		repo       AppSummaryRepository
	)

	BeforeEach(func() {
		getAppSummariesRequest := testapi.NewCloudControllerTestRequest(testnet.TestRequest{
			Method: "GET",
			Path:   "/v2/spaces/my-space-guid/summary",
			Response: testnet.TestResponse{
				Status: http.StatusOK,
				Body:   getAppSummariesResponseBody,
			},
		})

		testServer, handler = testnet.NewServer([]testnet.TestRequest{getAppSummariesRequest})
		configRepo := testconfig.NewRepositoryWithDefaults()
		configRepo.SetApiEndpoint(testServer.URL)
		gateway := net.NewCloudControllerGateway(configRepo, time.Now)
		repo = NewCloudControllerAppSummaryRepository(configRepo, gateway)
	})

	AfterEach(func() {
		testServer.Close()
	})

	It("returns a slice of app summaries for each instance", func() {
		apps, apiErr := repo.GetSummariesInCurrentSpace()
		Expect(handler).To(testnet.HaveAllRequestsCalled())

		Expect(apiErr).NotTo(HaveOccurred())
		Expect(2).To(Equal(len(apps)))

		app1 := apps[0]
		Expect(app1.Name).To(Equal("app1"))
		Expect(app1.Guid).To(Equal("app-1-guid"))
		Expect(len(app1.Routes)).To(Equal(1))
		Expect(app1.Routes[0].URL()).To(Equal("app1.cfapps.io"))

		Expect(app1.State).To(Equal("started"))
		Expect(app1.InstanceCount).To(Equal(1))
		Expect(app1.RunningInstances).To(Equal(1))
		Expect(app1.Memory).To(Equal(uint64(128)))

		app2 := apps[1]
		Expect(app2.Name).To(Equal("app2"))
		Expect(app2.Guid).To(Equal("app-2-guid"))
		Expect(len(app2.Routes)).To(Equal(2))
		Expect(app2.Routes[0].URL()).To(Equal("app2.cfapps.io"))
		Expect(app2.Routes[1].URL()).To(Equal("foo.cfapps.io"))

		Expect(app2.State).To(Equal("started"))
		Expect(app2.InstanceCount).To(Equal(3))
		Expect(app2.RunningInstances).To(Equal(1))
		Expect(app2.Memory).To(Equal(uint64(512)))
	})
})

const getAppSummariesResponseBody string = `
{
  "apps":[
    {
      "guid":"app-1-guid",
      "routes":[
        {
          "guid":"route-1-guid",
          "host":"app1",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        }
      ],
      "running_instances":1,
      "name":"app1",
      "memory":128,
      "instances":1,
      "state":"STARTED",
      "service_names":[
      	"my-service-instance"
      ]
    },{
      "guid":"app-2-guid",
      "routes":[
        {
          "guid":"route-2-guid",
          "host":"app2",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        },
        {
          "guid":"route-2-guid",
          "host":"foo",
          "domain":{
            "guid":"domain-1-guid",
            "name":"cfapps.io"
          }
        }
      ],
      "running_instances":1,
      "name":"app2",
      "memory":512,
      "instances":3,
      "state":"STARTED",
      "service_names":[
      	"my-service-instance"
      ]
    }
  ]
}`
<|endoftext|>


<commit_before>package api

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/cli/cf/configuration"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/gofileutils/fileutils"
	"io"
	"mime/multipart"
	"net/textproto"
	"os"
	"path/filepath"
	"time"
)

const (
	DefaultAppUploadBitsTimeout = 15 * time.Minute
)

type ApplicationBitsRepository interface {
	UploadApp(appGuid, dir string, cb func(path string, zipSize, fileCount uint64)) (apiErr error)
}

type CloudControllerApplicationBitsRepository struct {
	config  configuration.Reader
	gateway net.Gateway
	zipper  app_files.Zipper
}

func NewCloudControllerApplicationBitsRepository(config configuration.Reader, gateway net.Gateway, zipper app_files.Zipper) (repo CloudControllerApplicationBitsRepository) {
	repo.config = config
	repo.gateway = gateway
	repo.zipper = zipper
	return
}

func (repo CloudControllerApplicationBitsRepository) UploadApp(appGuid string, appDir string, fileSizePrinter func(path string, zipSize, fileCount uint64)) (apiErr error) {
	fileutils.TempDir("apps", func(uploadDir string, err error) {
		if err != nil {
			apiErr = err
			return
		}

		var presentFiles []resources.AppFileResource
		repo.sourceDir(appDir, func(sourceDir string, sourceErr error) {
			if sourceErr != nil {
				err = sourceErr
				return
			}
			presentFiles, err = repo.copyUploadableFiles(sourceDir, uploadDir)
		})

		if err != nil {
			apiErr = err
			return
		}

		fileutils.TempFile("uploads", func(zipFile *os.File, err error) {
			if err != nil {
				apiErr = err
				return
			}

			zipFileSize := uint64(0)
			zipFileCount := uint64(0)

			err = repo.zipper.Zip(uploadDir, zipFile)
			switch err := err.(type) {
			case nil:
				stat, err := zipFile.Stat()
				if err != nil {
					apiErr = errors.NewWithError(T("Error zipping application"), err)
					return
				}

				zipFileSize = uint64(stat.Size())
				zipFileCount = app_files.CountFiles(uploadDir)
			case *errors.EmptyDirError:
				zipFile = nil
			default:
				apiErr = errors.NewWithError(T("Error zipping application"), err)
				return
			}

			fileSizePrinter(appDir, zipFileSize, zipFileCount)

			apiErr = repo.uploadBits(appGuid, zipFile, presentFiles)
			if apiErr != nil {
				return
			}
		})
	})
	return
}

func (repo CloudControllerApplicationBitsRepository) uploadBits(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) (apiErr error) {
	url := fmt.Sprintf("%s/v2/apps/%s/bits", repo.config.ApiEndpoint(), appGuid)
	fileutils.TempFile("requests", func(requestFile *os.File, err error) {
		if err != nil {
			apiErr = errors.NewWithError(T("Error creating tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
			return
		}

		presentFilesJSON, err := json.Marshal(presentFiles)
		if err != nil {
			apiErr = errors.NewWithError(T("Error marshaling JSON"), err)
			return
		}

		boundary, err := repo.writeUploadBody(zipFile, requestFile, presentFilesJSON)
		if err != nil {
			apiErr = errors.NewWithError(T("Error writing to tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
			return
		}

		var request *net.Request
		request, apiErr = repo.gateway.NewRequest("PUT", url, repo.config.AccessToken(), requestFile)
		if apiErr != nil {
			return
		}

		contentType := fmt.Sprintf("multipart/form-data; boundary=%s", boundary)
		request.HttpReq.Header.Set("Content-Type", contentType)

		response := &resources.Resource{}
		_, apiErr = repo.gateway.PerformPollingRequestForJSONResponse(request, response, DefaultAppUploadBitsTimeout)
		if apiErr != nil {
			return
		}
	})

	return
}

func (repo CloudControllerApplicationBitsRepository) sourceDir(appDir string, cb func(sourceDir string, err error)) {
	// If appDir is a zip, first extract it to a temporary directory
	if repo.zipper.IsZipFile(appDir) {
		fileutils.TempDir("unzipped-app", func(tmpDir string, err error) {
			err = repo.extractZip(appDir, tmpDir)
			cb(tmpDir, err)
		})
	} else {
		cb(appDir, nil)
	}
}

func (repo CloudControllerApplicationBitsRepository) copyUploadableFiles(appDir string, uploadDir string) (presentFiles []resources.AppFileResource, err error) {
	// Find which files need to be uploaded
	allAppFiles, err := app_files.AppFilesInDir(appDir)
	if err != nil {
		return
	}

	appFilesToUpload, presentFiles, apiErr := repo.getFilesToUpload(allAppFiles)
	if apiErr != nil {
		err = errors.New(apiErr.Error())
		return
	}

	// Copy files into a temporary directory and return it
	err = app_files.CopyFiles(appFilesToUpload, appDir, uploadDir)
	if err != nil {
		return
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) extractZip(appDir, destDir string) (err error) {
	r, err := zip.OpenReader(appDir)
	if err != nil {
		return
	}
	defer r.Close()

	for _, f := range r.File {
		func() {
			// Don't try to extract directories
			if f.FileInfo().IsDir() {
				return
			}

			var rc io.ReadCloser
			rc, err = f.Open()
			if err != nil {
				return
			}

			// functional scope from above is important
			// otherwise this only closes the last file handle
			defer rc.Close()

			destFilePath := filepath.Join(destDir, f.Name)

			err = fileutils.CopyReaderToPath(rc, destFilePath)
			if err != nil {
				return
			}

			err = os.Chmod(destFilePath, f.FileInfo().Mode())
			if err != nil {
				return
			}
		}()
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) getFilesToUpload(allAppFiles []models.AppFileFields) (appFilesToUpload []models.AppFileFields, presentFiles []resources.AppFileResource, apiErr error) {
	appFilesRequest := []resources.AppFileResource{}
	for _, file := range allAppFiles {
		appFilesRequest = append(appFilesRequest, resources.AppFileResource{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		})
	}

	allAppFilesJson, err := json.Marshal(appFilesRequest)
	if err != nil {
		apiErr = errors.NewWithError(T("Failed to create json for resource_match request"), err)
		return
	}

	apiErr = repo.gateway.UpdateResourceSync(
		repo.config.ApiEndpoint()+"/v2/resource_match",
		bytes.NewReader(allAppFilesJson),
		&presentFiles)

	if apiErr != nil {
		return
	}

	appFilesToUpload = make([]models.AppFileFields, len(allAppFiles))
	copy(appFilesToUpload, allAppFiles)
	for _, file := range presentFiles {
		appFile := models.AppFileFields{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		}
		appFilesToUpload = repo.deleteAppFile(appFilesToUpload, appFile)
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) deleteAppFile(appFiles []models.AppFileFields, targetFile models.AppFileFields) []models.AppFileFields {
	for i, file := range appFiles {
		if file.Path == targetFile.Path {
			appFiles[i] = appFiles[len(appFiles)-1]
			return appFiles[:len(appFiles)-1]
		}
	}
	return appFiles
}

func (repo CloudControllerApplicationBitsRepository) writeUploadBody(zipFile *os.File, body *os.File, presentResourcesJson []byte) (boundary string, err error) {
	writer := multipart.NewWriter(body)
	defer writer.Close()

	boundary = writer.Boundary()

	part, err := writer.CreateFormField("resources")
	if err != nil {
		return
	}

	_, err = io.Copy(part, bytes.NewBuffer(presentResourcesJson))
	if err != nil {
		return
	}

	if zipFile != nil {
		zipStats, zipErr := zipFile.Stat()
		if zipErr != nil {
			return
		}

		if zipStats.Size() == 0 {
			return
		}

		part, zipErr = createZipPartWriter(zipStats, writer)
		if zipErr != nil {
			return
		}

		_, zipErr = io.Copy(part, zipFile)
		if zipErr != nil {
			return
		}
	}

	return
}

func createZipPartWriter(zipStats os.FileInfo, writer *multipart.Writer) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", `form-data; name="application"; filename="application.zip"`)
	h.Set("Content-Type", "application/zip")
	h.Set("Content-Length", fmt.Sprintf("%d", zipStats.Size()))
	h.Set("Content-Transfer-Encoding", "binary")
	return writer.CreatePart(h)
}
<commit_msg>Tweak import statement<commit_after>package api

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/textproto"
	"os"
	"path/filepath"
	"time"

	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/cli/cf/app_files"
	"github.com/cloudfoundry/cli/cf/configuration"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/net"
	"github.com/cloudfoundry/gofileutils/fileutils"
)

const (
	DefaultAppUploadBitsTimeout = 15 * time.Minute
)

type ApplicationBitsRepository interface {
	UploadApp(appGuid, dir string, cb func(path string, zipSize, fileCount uint64)) (apiErr error)
}

type CloudControllerApplicationBitsRepository struct {
	config  configuration.Reader
	gateway net.Gateway
	zipper  app_files.Zipper
}

func NewCloudControllerApplicationBitsRepository(config configuration.Reader, gateway net.Gateway, zipper app_files.Zipper) (repo CloudControllerApplicationBitsRepository) {
	repo.config = config
	repo.gateway = gateway
	repo.zipper = zipper
	return
}

func (repo CloudControllerApplicationBitsRepository) UploadApp(appGuid string, appDir string, fileSizePrinter func(path string, zipSize, fileCount uint64)) (apiErr error) {
	fileutils.TempDir("apps", func(uploadDir string, err error) {
		if err != nil {
			apiErr = err
			return
		}

		var presentFiles []resources.AppFileResource
		repo.sourceDir(appDir, func(sourceDir string, sourceErr error) {
			if sourceErr != nil {
				err = sourceErr
				return
			}
			presentFiles, err = repo.copyUploadableFiles(sourceDir, uploadDir)
		})

		if err != nil {
			apiErr = err
			return
		}

		fileutils.TempFile("uploads", func(zipFile *os.File, err error) {
			if err != nil {
				apiErr = err
				return
			}

			zipFileSize := uint64(0)
			zipFileCount := uint64(0)

			err = repo.zipper.Zip(uploadDir, zipFile)
			switch err := err.(type) {
			case nil:
				stat, err := zipFile.Stat()
				if err != nil {
					apiErr = errors.NewWithError(T("Error zipping application"), err)
					return
				}

				zipFileSize = uint64(stat.Size())
				zipFileCount = app_files.CountFiles(uploadDir)
			case *errors.EmptyDirError:
				zipFile = nil
			default:
				apiErr = errors.NewWithError(T("Error zipping application"), err)
				return
			}

			fileSizePrinter(appDir, zipFileSize, zipFileCount)

			apiErr = repo.uploadBits(appGuid, zipFile, presentFiles)
			if apiErr != nil {
				return
			}
		})
	})
	return
}

func (repo CloudControllerApplicationBitsRepository) uploadBits(appGuid string, zipFile *os.File, presentFiles []resources.AppFileResource) (apiErr error) {
	url := fmt.Sprintf("%s/v2/apps/%s/bits", repo.config.ApiEndpoint(), appGuid)
	fileutils.TempFile("requests", func(requestFile *os.File, err error) {
		if err != nil {
			apiErr = errors.NewWithError(T("Error creating tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
			return
		}

		presentFilesJSON, err := json.Marshal(presentFiles)
		if err != nil {
			apiErr = errors.NewWithError(T("Error marshaling JSON"), err)
			return
		}

		boundary, err := repo.writeUploadBody(zipFile, requestFile, presentFilesJSON)
		if err != nil {
			apiErr = errors.NewWithError(T("Error writing to tmp file: {{.Err}}", map[string]interface{}{"Err": err}), err)
			return
		}

		var request *net.Request
		request, apiErr = repo.gateway.NewRequest("PUT", url, repo.config.AccessToken(), requestFile)
		if apiErr != nil {
			return
		}

		contentType := fmt.Sprintf("multipart/form-data; boundary=%s", boundary)
		request.HttpReq.Header.Set("Content-Type", contentType)

		response := &resources.Resource{}
		_, apiErr = repo.gateway.PerformPollingRequestForJSONResponse(request, response, DefaultAppUploadBitsTimeout)
		if apiErr != nil {
			return
		}
	})

	return
}

func (repo CloudControllerApplicationBitsRepository) sourceDir(appDir string, cb func(sourceDir string, err error)) {
	// If appDir is a zip, first extract it to a temporary directory
	if repo.zipper.IsZipFile(appDir) {
		fileutils.TempDir("unzipped-app", func(tmpDir string, err error) {
			err = repo.extractZip(appDir, tmpDir)
			cb(tmpDir, err)
		})
	} else {
		cb(appDir, nil)
	}
}

func (repo CloudControllerApplicationBitsRepository) copyUploadableFiles(appDir string, uploadDir string) (presentFiles []resources.AppFileResource, err error) {
	// Find which files need to be uploaded
	allAppFiles, err := app_files.AppFilesInDir(appDir)
	if err != nil {
		return
	}

	appFilesToUpload, presentFiles, apiErr := repo.getFilesToUpload(allAppFiles)
	if apiErr != nil {
		err = errors.New(apiErr.Error())
		return
	}

	// Copy files into a temporary directory and return it
	err = app_files.CopyFiles(appFilesToUpload, appDir, uploadDir)
	if err != nil {
		return
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) extractZip(appDir, destDir string) (err error) {
	r, err := zip.OpenReader(appDir)
	if err != nil {
		return
	}
	defer r.Close()

	for _, f := range r.File {
		func() {
			// Don't try to extract directories
			if f.FileInfo().IsDir() {
				return
			}

			var rc io.ReadCloser
			rc, err = f.Open()
			if err != nil {
				return
			}

			// functional scope from above is important
			// otherwise this only closes the last file handle
			defer rc.Close()

			destFilePath := filepath.Join(destDir, f.Name)

			err = fileutils.CopyReaderToPath(rc, destFilePath)
			if err != nil {
				return
			}

			err = os.Chmod(destFilePath, f.FileInfo().Mode())
			if err != nil {
				return
			}
		}()
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) getFilesToUpload(allAppFiles []models.AppFileFields) (appFilesToUpload []models.AppFileFields, presentFiles []resources.AppFileResource, apiErr error) {
	appFilesRequest := []resources.AppFileResource{}
	for _, file := range allAppFiles {
		appFilesRequest = append(appFilesRequest, resources.AppFileResource{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		})
	}

	allAppFilesJson, err := json.Marshal(appFilesRequest)
	if err != nil {
		apiErr = errors.NewWithError(T("Failed to create json for resource_match request"), err)
		return
	}

	apiErr = repo.gateway.UpdateResourceSync(
		repo.config.ApiEndpoint()+"/v2/resource_match",
		bytes.NewReader(allAppFilesJson),
		&presentFiles)

	if apiErr != nil {
		return
	}

	appFilesToUpload = make([]models.AppFileFields, len(allAppFiles))
	copy(appFilesToUpload, allAppFiles)
	for _, file := range presentFiles {
		appFile := models.AppFileFields{
			Path: file.Path,
			Sha1: file.Sha1,
			Size: file.Size,
		}
		appFilesToUpload = repo.deleteAppFile(appFilesToUpload, appFile)
	}

	return
}

func (repo CloudControllerApplicationBitsRepository) deleteAppFile(appFiles []models.AppFileFields, targetFile models.AppFileFields) []models.AppFileFields {
	for i, file := range appFiles {
		if file.Path == targetFile.Path {
			appFiles[i] = appFiles[len(appFiles)-1]
			return appFiles[:len(appFiles)-1]
		}
	}
	return appFiles
}

func (repo CloudControllerApplicationBitsRepository) writeUploadBody(zipFile *os.File, body *os.File, presentResourcesJson []byte) (boundary string, err error) {
	writer := multipart.NewWriter(body)
	defer writer.Close()

	boundary = writer.Boundary()

	part, err := writer.CreateFormField("resources")
	if err != nil {
		return
	}

	_, err = io.Copy(part, bytes.NewBuffer(presentResourcesJson))
	if err != nil {
		return
	}

	if zipFile != nil {
		zipStats, zipErr := zipFile.Stat()
		if zipErr != nil {
			return
		}

		if zipStats.Size() == 0 {
			return
		}

		part, zipErr = createZipPartWriter(zipStats, writer)
		if zipErr != nil {
			return
		}

		_, zipErr = io.Copy(part, zipFile)
		if zipErr != nil {
			return
		}
	}

	return
}

func createZipPartWriter(zipStats os.FileInfo, writer *multipart.Writer) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition", `form-data; name="application"; filename="application.zip"`)
	h.Set("Content-Type", "application/zip")
	h.Set("Content-Length", fmt.Sprintf("%d", zipStats.Size()))
	h.Set("Content-Transfer-Encoding", "binary")
	return writer.CreatePart(h)
}
<|endoftext|>