<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains experimental functions for interacting with the Discord
// Voice websocket and UDP connections.
//
// EVERYTHING in this file is very experimental and will change.

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/gorilla/websocket"
)

// A VEvent is the initial structure for voice websocket events.  I think
// I can reuse the data websocket structure here.
type VEvent struct {
	Type      string          `json:"t"`
	State     int             `json:"s"`
	Operation int             `json:"op"`
	RawData   json.RawMessage `json:"d"`
}

// A VoiceOP2 stores the data for voice operation 2 websocket events
// which is sort of like the voice READY packet
type VoiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// VoiceOpenWS opens a voice websocket connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (s *Session) VoiceOpenWS() {

	// Don't open a socket if one is already open
	if s.VwsConn != nil {
		return
	}

	var self User
	var err error

	self, err = s.User("@me") // TODO: Move to @ login and store in session

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(s.VEndpoint, ":80"))
	s.VwsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE cannot open websocket:", err)
	}

	json := voiceHandshakeOp{0, voiceHandshakeData{s.VGuildID, self.ID, s.VSessionID, s.VToken}}

	err = s.VwsConn.WriteJSON(json)
	if err != nil {
		fmt.Println("VOICE ERROR sending init packet:", err)
	}

	// Start a listening for voice websocket events
	go s.VoiceListen()
}

// Close closes the connection to the voice websocket.
func (s *Session) VoiceCloseWS() {
	s.VwsConn.Close()
}

// VoiceListen listens on the voice websocket for messages and passes them
// to the voice event handler.
func (s *Session) VoiceListen() (err error) {

	for {
		messageType, message, err := s.VwsConn.ReadMessage()
		if err != nil {
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go s.VoiceEvent(messageType, message)
	}

	return
}

// VoiceEvent handles any messages received on the voice websocket
func (s *Session) VoiceEvent(messageType int, message []byte) (err error) {

	if s.Debug {
		fmt.Println("VOICE EVENT:", messageType)
		printJSON(message)
	}

	var e VEvent
	if err := json.Unmarshal(message, &e); err != nil {
		return err
	}

	switch e.Operation {

	case 2: // READY packet
		var st VoiceOP2
		if err := json.Unmarshal(e.RawData, &st); err != nil {
			fmt.Println(e.Type, err)
			printJSON(e.RawData) // TODO: Better error logginEventg
			return err
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go s.VoiceHeartbeat(st.HeartbeatInterval)

		// Store all event data into the session
		s.Vop2 = st

		// We now have enough data to start the UDP connection
		s.VoiceOpenUDP()

		return
	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return
	case 4:
		// TODO
	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// VoiceOpenUDP opens a UDP connect to the voice server and completes the
// initial required handshake.  This connect is left open in the session
// and can be used to send or receive audio.
func (s *Session) VoiceOpenUDP() {

	udpHost := fmt.Sprintf("%s:%d", strings.TrimSuffix(s.VEndpoint, ":80"), s.Vop2.Port)
	serverAddr, err := net.ResolveUDPAddr("udp", udpHost)
	if err != nil {
		fmt.Println(err)
	}

	s.UDPConn, err = net.DialUDP("udp", nil, serverAddr)
	if err != nil {
		fmt.Println(err)
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, s.Vop2.SSRC)
	s.UDPConn.Write(sb)

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := s.UDPConn.ReadFromUDP(rb)
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from postion 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the parsed data from above and send it back to Discord
	// to finalize the UDP handshake.
	jsondata := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = s.VwsConn.WriteJSON(jsondata)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	s.UDPReady = true
}

// VoiceCloseUDP closes the voice UDP connection.
func (s *Session) VoiceCloseUDP() {
	s.UDPConn.Close()
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

func (s *Session) VoiceSpeaking() {

	if s.VwsConn == nil {
		// TODO return an error
		fmt.Println("No Voice websocket.")
		return
	}

	json := voiceSpeakingOp{5, voiceSpeakingData{true, 0}}
	err := s.VwsConn.WriteJSON(json)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
}

// VoiceListenUDP is test code to listen for UDP packets
func (s *Session) VoiceListenUDP() {

	// start the udp keep alive too.  Otherwise listening doesn't get much.
	// THIS DOES NOT WORK YET
	// go s.VoiceUDPKeepalive(s.Vop2.HeartbeatInterval) // lets try the ws timer

	for {
		b := make([]byte, 1024) //TODO DO NOT PUT MAKE INSIDE LOOP
		rlen, _, err := s.UDPConn.ReadFromUDP(b)
		if err != nil {
			fmt.Println("Error reading from UDP:", err)
			//			return
		}

		if rlen < 1 {
			fmt.Println("Empty UDP packet received")
			continue
			// empty packet?
		}
		fmt.Println("READ FROM UDP: ", b)
	}

}

// VoiceUDPKeepalive sends a packet to keep the UDP connection forwarding
// alive for NATed clients.  Without this no audio can be received
// after short periods of silence.
// Not sure how often this is supposed to be sent or even what payload
// I am suppose to be sending.  So this is very.. unfinished :)
func (s *Session) VoiceUDPKeepalive(i time.Duration) {

	// NONE OF THIS WORKS. SO DON'T USE IT.
	//
	// testing with the above 70 byte SSRC packet.
	//
	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord

	ticker := time.NewTicker(i * time.Millisecond)
	for _ = range ticker.C {
		sb := make([]byte, 8)
		sb[0] = 0x80
		sb[1] = 0xc9
		sb[2] = 0x00
		sb[3] = 0x01

		ssrcBE := make([]byte, 4)
		binary.BigEndian.PutUint32(ssrcBE, s.Vop2.SSRC)

		sb[4] = ssrcBE[0]
		sb[5] = ssrcBE[1]
		sb[6] = ssrcBE[2]
		sb[7] = ssrcBE[3]

		s.UDPConn.Write(ssrcBE)
	}
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// VoiceHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (s *Session) VoiceHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := s.VwsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			s.VoiceReady = false
			fmt.Println(err)
			return // TODO LOG ERROR
		}

		s.VoiceReady = true
		<-ticker.C
	}
}
<commit_msg>The start of the voice rework begins.<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains experimental functions for interacting with the Discord
// Voice websocket and UDP connections.
//
// EVERYTHING in this file is very experimental and will change.

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// A Voice struct holds all data and functions related to Discord Voice support.
// NOTE: This is not used right at this moment, but it will be used soon.
type voice struct {
	Ready bool
	WS    *voiceWS
	UDP   *voiceUDP

	SessionID string
	Token     string
	Endpoint  string
	GuildID   string
	ChannelID string
	OP2       *voiceOP2
}

type voiceWS struct {
	Ready bool
	Chan  chan struct{}
	Lock  sync.Mutex
	Conn  *websocket.Conn
}

type voiceUDP struct {
	Ready bool
	Chan  chan struct{}
	Lock  sync.Mutex
	Conn  *net.UDPConn
}

// A voiceOP2 stores the data for voice operation 2 websocket events
// which is sort of like the voice READY packet
type voiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// VoiceOpenWS opens a voice websocket connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (s *Session) VoiceOpenWS() {

	// Don't open a socket if one is already open
	if s.VwsConn != nil {
		return
	}

	var self User
	var err error

	self, err = s.User("@me") // TODO: Move to @ login and store in session

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(s.VEndpoint, ":80"))
	s.VwsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE cannot open websocket:", err)
	}

	json := voiceHandshakeOp{0, voiceHandshakeData{s.VGuildID, self.ID, s.VSessionID, s.VToken}}

	err = s.VwsConn.WriteJSON(json)
	if err != nil {
		fmt.Println("VOICE ERROR sending init packet:", err)
	}

	// Start a listening for voice websocket events
	go s.VoiceListen()
}

// Close closes the connection to the voice websocket.
func (s *Session) VoiceCloseWS() {
	s.VwsConn.Close()
}

// VoiceListen listens on the voice websocket for messages and passes them
// to the voice event handler.
func (s *Session) VoiceListen() (err error) {

	for {
		messageType, message, err := s.VwsConn.ReadMessage()
		if err != nil {
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go s.VoiceEvent(messageType, message)
	}

	return
}

// VoiceEvent handles any messages received on the voice websocket
func (s *Session) VoiceEvent(messageType int, message []byte) (err error) {

	if s.Debug {
		fmt.Println("VOICE EVENT:", messageType)
		printJSON(message)
	}

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		return err
	}

	switch e.Operation {

	case 2: // READY packet
		var st voiceOP2
		if err := json.Unmarshal(e.RawData, &st); err != nil {
			fmt.Println(e.Type, err)
			printJSON(e.RawData) // TODO: Better error logginEventg
			return err
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go s.VoiceHeartbeat(st.HeartbeatInterval)

		// Store all event data into the session
		s.Vop2 = st

		// We now have enough data to start the UDP connection
		s.VoiceOpenUDP()

		return
	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return
	case 4:
		// TODO
	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// VoiceOpenUDP opens a UDP connect to the voice server and completes the
// initial required handshake.  This connect is left open in the session
// and can be used to send or receive audio.
func (s *Session) VoiceOpenUDP() {

	udpHost := fmt.Sprintf("%s:%d", strings.TrimSuffix(s.VEndpoint, ":80"), s.Vop2.Port)
	serverAddr, err := net.ResolveUDPAddr("udp", udpHost)
	if err != nil {
		fmt.Println(err)
	}

	s.UDPConn, err = net.DialUDP("udp", nil, serverAddr)
	if err != nil {
		fmt.Println(err)
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, s.Vop2.SSRC)
	s.UDPConn.Write(sb)

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := s.UDPConn.ReadFromUDP(rb)
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from postion 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the parsed data from above and send it back to Discord
	// to finalize the UDP handshake.
	jsondata := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = s.VwsConn.WriteJSON(jsondata)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	s.UDPReady = true
}

// VoiceCloseUDP closes the voice UDP connection.
func (s *Session) VoiceCloseUDP() {
	s.UDPConn.Close()
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

func (s *Session) VoiceSpeaking() {

	if s.VwsConn == nil {
		// TODO return an error
		fmt.Println("No Voice websocket.")
		return
	}

	json := voiceSpeakingOp{5, voiceSpeakingData{true, 0}}
	err := s.VwsConn.WriteJSON(json)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
}

// VoiceListenUDP is test code to listen for UDP packets
func (s *Session) VoiceListenUDP() {

	// start the udp keep alive too.  Otherwise listening doesn't get much.
	// THIS DOES NOT WORK YET
	// go s.VoiceUDPKeepalive(s.Vop2.HeartbeatInterval) // lets try the ws timer

	for {
		b := make([]byte, 1024) //TODO DO NOT PUT MAKE INSIDE LOOP
		rlen, _, err := s.UDPConn.ReadFromUDP(b)
		if err != nil {
			fmt.Println("Error reading from UDP:", err)
			//			return
		}

		if rlen < 1 {
			fmt.Println("Empty UDP packet received")
			continue
			// empty packet?
		}
		fmt.Println("READ FROM UDP: ", b)
	}

}

// VoiceUDPKeepalive sends a packet to keep the UDP connection forwarding
// alive for NATed clients.  Without this no audio can be received
// after short periods of silence.
// Not sure how often this is supposed to be sent or even what payload
// I am suppose to be sending.  So this is very.. unfinished :)
func (s *Session) VoiceUDPKeepalive(i time.Duration) {

	// NONE OF THIS WORKS. SO DON'T USE IT.
	//
	// testing with the above 70 byte SSRC packet.
	//
	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord

	ticker := time.NewTicker(i * time.Millisecond)
	for _ = range ticker.C {
		sb := make([]byte, 8)
		sb[0] = 0x80
		sb[1] = 0xc9
		sb[2] = 0x00
		sb[3] = 0x01

		ssrcBE := make([]byte, 4)
		binary.BigEndian.PutUint32(ssrcBE, s.Vop2.SSRC)

		sb[4] = ssrcBE[0]
		sb[5] = ssrcBE[1]
		sb[6] = ssrcBE[2]
		sb[7] = ssrcBE[3]

		s.UDPConn.Write(ssrcBE)
	}
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// VoiceHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (s *Session) VoiceHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := s.VwsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			s.VoiceReady = false
			fmt.Println(err)
			return // TODO LOG ERROR
		}

		s.VoiceReady = true
		<-ticker.C
	}
}
<|endoftext|>


<commit_before>package netdicom

// Implements message types defined in P3.7.
//
// http://dicom.nema.org/medical/dicom/current/output/pdf/part07.pdf

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/yasushi-saito/go-dicom"
	"io"
	"log"
)

type DIMSEMessage interface {
	Encode(*dicom.Encoder)
	HasData() bool
	String() string
}

func findElementWithTag(elems []*dicom.DicomElement, tag dicom.Tag) (*dicom.DicomElement, error) {
	for _, elem := range elems {
		if elem.Tag == tag {
			log.Printf("Return %v for %s", elem, tag.String())
			return elem, nil
		}
	}

	return nil, fmt.Errorf("Element %s not found during DIMSE decoding", tag.String())
}

func getStringFromElements(elems []*dicom.DicomElement, tag dicom.Tag) (string, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return "", err
	}
	return e.GetString()
}

func getUInt32FromElements(elems []*dicom.DicomElement, tag dicom.Tag) (uint32, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return 0, err
	}
	return e.GetUInt32()
}

func getUInt16FromElements(elems []*dicom.DicomElement, tag dicom.Tag) (uint16, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return 0, err
	}
	return e.GetUInt16()
}

// Fields common to all DIMSE messages.
type DIMSEMessageHeader struct {
	CommandField uint16 // (0000,0100)
}

func encodeDataElementWithSingleValue(e *dicom.Encoder, tag dicom.Tag, v interface{}) {
	elem := dicom.DicomElement{
		Tag:   tag,
		Vr:    "", // autodetect
		Vl:    1,
		Value: []interface{}{v},
	}
	dicom.EncodeDataElement(e, &elem)
}

func encodeDIMSEMessageHeader(e *dicom.Encoder, v DIMSEMessageHeader) {
	//encodeDataElementWithSingleValue(e, dicom.Tag{0, 0}, v.CommandGroupLength)
	//encodeDataElementWithSingleValue(e, dicom.Tag{0, 2}, v.AffectedSOPClassUID)
}

// P3.7 9.3.1.1
type C_STORE_RQ struct {
	AffectedSOPClassUID                  string
	MessageID                            uint16
	Priority                             uint16
	CommandDataSetType                   uint16
	AffectedSOPInstanceUID               string
	MoveOriginatorApplicationEntityTitle string
	MoveOriginatorMessageID              uint16
}

func (v *C_STORE_RQ) HasData() bool {
	doassert(v.CommandDataSetType != CommandDataSetTypeNull) // TODO(saito)
	return v.CommandDataSetType != CommandDataSetTypeNull
}

func (v *C_STORE_RQ) Encode(e *dicom.Encoder) {
	encodeDataElementWithSingleValue(e, dicom.TagCommandField, uint16(1))
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPClassUID, v.AffectedSOPClassUID)
	encodeDataElementWithSingleValue(e, dicom.TagMessageID, v.MessageID)
	encodeDataElementWithSingleValue(e, dicom.TagPriority, v.Priority)
	encodeDataElementWithSingleValue(e, dicom.TagCommandDataSetType, v.CommandDataSetType)
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPInstanceUID, v.AffectedSOPInstanceUID)
	if v.MoveOriginatorApplicationEntityTitle != "" {
		encodeDataElementWithSingleValue(e, dicom.Tag{0, 1030}, v.MoveOriginatorApplicationEntityTitle)
	}
	if v.MoveOriginatorMessageID != 0 {
		encodeDataElementWithSingleValue(e, dicom.Tag{0, 1031}, v.MoveOriginatorMessageID)
	}
}

func decodeC_STORE_RQ(elems []*dicom.DicomElement) (*C_STORE_RQ, error) {
	v := C_STORE_RQ{}
	var err error
	v.AffectedSOPClassUID, err = getStringFromElements(elems, dicom.TagAffectedSOPClassUID)
	if err != nil {
		return nil, err
	}
	v.MessageID, err = getUInt16FromElements(elems, dicom.TagMessageID)
	if err != nil {
		return nil, err
	}
	v.Priority, err = getUInt16FromElements(elems, dicom.TagPriority)
	if err != nil {
		return nil, err
	}
	v.CommandDataSetType, err = getUInt16FromElements(elems, dicom.TagCommandDataSetType)
	if err != nil {
		return nil, err
	}
	v.AffectedSOPInstanceUID, err = getStringFromElements(elems, dicom.TagAffectedSOPInstanceUID)
	if err != nil {
		return nil, err
	}
	v.MoveOriginatorApplicationEntityTitle, _ = getStringFromElements(elems, dicom.TagMoveOriginatorApplicationEntityTitle)
	v.MoveOriginatorMessageID, _ = getUInt16FromElements(elems, dicom.TagMoveOriginatorMessageID)
	return &v, nil
}

func (v *C_STORE_RQ) String() string {
	return fmt.Sprintf("cstorerq{sopclass:%v messageid:%v pri: %v cmddatasettype: %v sopinstance: %v m0:%v m1:%v}",
		v.AffectedSOPClassUID, v.MessageID, v.Priority, v.CommandDataSetType, v.AffectedSOPInstanceUID,
		v.MoveOriginatorApplicationEntityTitle, v.MoveOriginatorMessageID)
}

const CommandDataSetTypeNull uint16 = 0x101

// P3.7 9.3.1.2
type C_STORE_RSP struct {
	AffectedSOPClassUID       string
	MessageIDBeingRespondedTo uint16
	// CommandDataSetType shall always be 0x0101; RSP has no dataset.
	CommandDataSetType     uint16
	AffectedSOPInstanceUID string
	Status                 uint16
}

// C_STORE_RSP status codes.
// P3.4 GG4-1
const (
	CStoreStatusOutOfResources              uint16 = 0xa700
	CStoreStatusDataSetDoesNotMatchSOPClass uint16 = 0xa900
	CStoreStatusCannotUnderstand            uint16 = 0xc000
)

// P3.7 C
func decodeC_STORE_RSP(elems []*dicom.DicomElement) (*C_STORE_RSP, error) {
	v := &C_STORE_RSP{}
	var err error
	v.AffectedSOPClassUID, err = getStringFromElements(elems, dicom.TagAffectedSOPClassUID)
	if err != nil {
		return nil, err
	}
	v.MessageIDBeingRespondedTo, err = getUInt16FromElements(elems, dicom.TagMessageIDBeingRespondedTo)
	if err != nil {
		return nil, err
	}
	v.Status, err = getUInt16FromElements(elems, dicom.TagStatus)
	if err != nil {
		return nil, err
	}
	v.CommandDataSetType, err = getUInt16FromElements(elems, dicom.TagCommandDataSetType)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (v *C_STORE_RSP) Encode(e *dicom.Encoder) {
	doassert(v.CommandDataSetType == 0x101)
	encodeDataElementWithSingleValue(e, dicom.TagCommandField, uint16(0x8001))
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPClassUID, v.AffectedSOPClassUID)
	encodeDataElementWithSingleValue(e, dicom.TagMessageIDBeingRespondedTo, v.MessageIDBeingRespondedTo)
	encodeDataElementWithSingleValue(e, dicom.TagCommandDataSetType, v.CommandDataSetType)
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPInstanceUID, v.AffectedSOPInstanceUID)
	encodeDataElementWithSingleValue(e, dicom.TagStatus, v.Status)
}

func (v *C_STORE_RSP) HasData() bool {
	doassert(v.CommandDataSetType == CommandDataSetTypeNull) // TODO(saito)
	return v.CommandDataSetType != CommandDataSetTypeNull
}

func (v *C_STORE_RSP) String() string {
	return fmt.Sprintf("cstorersp{sopclass:%v messageid:%v cmddatasettype: %v sopinstance: %v status: 0x%v}",
		v.AffectedSOPClassUID, v.MessageIDBeingRespondedTo, v.CommandDataSetType, v.AffectedSOPInstanceUID,
		v.Status)
}

func ReadDIMSEMessage(io io.Reader, limit int64) (DIMSEMessage, error) {
	var elems []*dicom.DicomElement
	// Note: DIMSE elements are always implicit LE.
	//
	// TODO(saito) make sure that's the case. Where the ref?
	d := dicom.NewDecoder(io, limit, binary.LittleEndian, dicom.ImplicitVR)
	for d.Len() > 0 {
		elem := dicom.ReadDataElement(d)
		if d.Error() != nil {
			break
		}
		elems = append(elems, elem)
	}
	if err := d.Finish(); err != nil {
		return nil, err
	}

	commandField, err := getUInt16FromElements(elems, dicom.TagCommandField)
	if err != nil {
		return nil, err
	}
	switch commandField {
	case 1:
		return decodeC_STORE_RQ(elems)
	case 0x8001:
		return decodeC_STORE_RSP(elems)
	}
	log.Panicf("Unknown DIMSE command 0x%x", commandField)
	return nil, err
}

func encodeDIMSEMessage(v DIMSEMessage) ([]byte, error) {
	// DIMSE messages are always encoded Implicit+LE. See P3.7 6.3.1.
	subEncoder := dicom.NewEncoder(binary.LittleEndian, dicom.ImplicitVR)
	v.Encode(subEncoder)
	bytes, err := subEncoder.Finish()
	if err != nil {
		return nil, err
	}

	e := dicom.NewEncoder(binary.LittleEndian, dicom.ImplicitVR)
	encodeDataElementWithSingleValue(e, dicom.TagCommandGroupLength, uint32(len(bytes)))
	e.WriteBytes(bytes)
	return e.Finish()
}

type dimseCommandAssembler struct {
	contextID      byte
	commandBytes   []byte
	command        DIMSEMessage
	dataBytes      []byte
	readAllCommand bool

	readAllData bool
}

func addPDataTF(a *dimseCommandAssembler, pdu *P_DATA_TF, contextManager *contextManager) (string, string, DIMSEMessage, []byte, error) {
	for _, item := range pdu.Items {
		if a.contextID == 0 {
			a.contextID = item.ContextID
		} else if a.contextID != item.ContextID {
			// TODO(saito) don't panic here.
			log.Panicf("Mixed context: %d %d", a.contextID, item.ContextID)
		}
		if item.Command {
			a.commandBytes = append(a.commandBytes, item.Value...)
			if item.Last {
				doassert(!a.readAllCommand)
				a.readAllCommand = true
			}
		} else {
			a.dataBytes = append(a.dataBytes, item.Value...)
			if item.Last {
				doassert(!a.readAllData)
				a.readAllData = true
			}
		}
	}
	if !a.readAllCommand {
		return "", "", nil, nil, nil
	}
	if a.command == nil {
		var err error
		a.command, err = ReadDIMSEMessage(bytes.NewBuffer(a.commandBytes), int64(len(a.commandBytes)))
		if err != nil {
			return "", "", nil, nil, err
		}
	}
	if a.command.HasData() && !a.readAllData {
		return "", "", nil, nil, nil
	}
	context, err := contextManager.lookupByContextID(a.contextID)
	if err != nil {
		log.Panic(err)
	}
	command := a.command
	dataBytes := a.dataBytes
	log.Printf("Read all data for syntax %s, command [%v], data %d bytes, err%v",
		dicom.UIDString(context.abstractSyntaxUID),
		command.String(), len(a.dataBytes), err)
	*a = dimseCommandAssembler{}
	return context.abstractSyntaxUID, context.transferSyntaxUID, command, dataBytes, nil
	// TODO(saito) Verify that there's no unread items after the last command&data.
}
<commit_msg>More removing of panics.<commit_after>package netdicom

// Implements message types defined in P3.7.
//
// http://dicom.nema.org/medical/dicom/current/output/pdf/part07.pdf

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/yasushi-saito/go-dicom"
	"io"
	"log"
)

type DIMSEMessage interface {
	Encode(*dicom.Encoder)
	HasData() bool
	String() string
}

func findElementWithTag(elems []*dicom.DicomElement, tag dicom.Tag) (*dicom.DicomElement, error) {
	for _, elem := range elems {
		if elem.Tag == tag {
			log.Printf("Return %v for %s", elem, tag.String())
			return elem, nil
		}
	}

	return nil, fmt.Errorf("Element %s not found during DIMSE decoding", tag.String())
}

func getStringFromElements(elems []*dicom.DicomElement, tag dicom.Tag) (string, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return "", err
	}
	return e.GetString()
}

func getUInt32FromElements(elems []*dicom.DicomElement, tag dicom.Tag) (uint32, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return 0, err
	}
	return e.GetUInt32()
}

func getUInt16FromElements(elems []*dicom.DicomElement, tag dicom.Tag) (uint16, error) {
	e, err := findElementWithTag(elems, tag)
	if err != nil {
		return 0, err
	}
	return e.GetUInt16()
}

// Fields common to all DIMSE messages.
type DIMSEMessageHeader struct {
	CommandField uint16 // (0000,0100)
}

func encodeDataElementWithSingleValue(e *dicom.Encoder, tag dicom.Tag, v interface{}) {
	elem := dicom.DicomElement{
		Tag:   tag,
		Vr:    "", // autodetect
		Vl:    1,
		Value: []interface{}{v},
	}
	dicom.EncodeDataElement(e, &elem)
}

func encodeDIMSEMessageHeader(e *dicom.Encoder, v DIMSEMessageHeader) {
	//encodeDataElementWithSingleValue(e, dicom.Tag{0, 0}, v.CommandGroupLength)
	//encodeDataElementWithSingleValue(e, dicom.Tag{0, 2}, v.AffectedSOPClassUID)
}

// P3.7 9.3.1.1
type C_STORE_RQ struct {
	AffectedSOPClassUID                  string
	MessageID                            uint16
	Priority                             uint16
	CommandDataSetType                   uint16
	AffectedSOPInstanceUID               string
	MoveOriginatorApplicationEntityTitle string
	MoveOriginatorMessageID              uint16
}

func (v *C_STORE_RQ) HasData() bool {
	doassert(v.CommandDataSetType != CommandDataSetTypeNull) // TODO(saito)
	return v.CommandDataSetType != CommandDataSetTypeNull
}

func (v *C_STORE_RQ) Encode(e *dicom.Encoder) {
	encodeDataElementWithSingleValue(e, dicom.TagCommandField, uint16(1))
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPClassUID, v.AffectedSOPClassUID)
	encodeDataElementWithSingleValue(e, dicom.TagMessageID, v.MessageID)
	encodeDataElementWithSingleValue(e, dicom.TagPriority, v.Priority)
	encodeDataElementWithSingleValue(e, dicom.TagCommandDataSetType, v.CommandDataSetType)
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPInstanceUID, v.AffectedSOPInstanceUID)
	if v.MoveOriginatorApplicationEntityTitle != "" {
		encodeDataElementWithSingleValue(e, dicom.Tag{0, 1030}, v.MoveOriginatorApplicationEntityTitle)
	}
	if v.MoveOriginatorMessageID != 0 {
		encodeDataElementWithSingleValue(e, dicom.Tag{0, 1031}, v.MoveOriginatorMessageID)
	}
}

func decodeC_STORE_RQ(elems []*dicom.DicomElement) (*C_STORE_RQ, error) {
	v := C_STORE_RQ{}
	var err error
	v.AffectedSOPClassUID, err = getStringFromElements(elems, dicom.TagAffectedSOPClassUID)
	if err != nil {
		return nil, err
	}
	v.MessageID, err = getUInt16FromElements(elems, dicom.TagMessageID)
	if err != nil {
		return nil, err
	}
	v.Priority, err = getUInt16FromElements(elems, dicom.TagPriority)
	if err != nil {
		return nil, err
	}
	v.CommandDataSetType, err = getUInt16FromElements(elems, dicom.TagCommandDataSetType)
	if err != nil {
		return nil, err
	}
	v.AffectedSOPInstanceUID, err = getStringFromElements(elems, dicom.TagAffectedSOPInstanceUID)
	if err != nil {
		return nil, err
	}
	v.MoveOriginatorApplicationEntityTitle, _ = getStringFromElements(elems, dicom.TagMoveOriginatorApplicationEntityTitle)
	v.MoveOriginatorMessageID, _ = getUInt16FromElements(elems, dicom.TagMoveOriginatorMessageID)
	return &v, nil
}

func (v *C_STORE_RQ) String() string {
	return fmt.Sprintf("cstorerq{sopclass:%v messageid:%v pri: %v cmddatasettype: %v sopinstance: %v m0:%v m1:%v}",
		v.AffectedSOPClassUID, v.MessageID, v.Priority, v.CommandDataSetType, v.AffectedSOPInstanceUID,
		v.MoveOriginatorApplicationEntityTitle, v.MoveOriginatorMessageID)
}

const CommandDataSetTypeNull uint16 = 0x101

// P3.7 9.3.1.2
type C_STORE_RSP struct {
	AffectedSOPClassUID       string
	MessageIDBeingRespondedTo uint16
	// CommandDataSetType shall always be 0x0101; RSP has no dataset.
	CommandDataSetType     uint16
	AffectedSOPInstanceUID string
	Status                 uint16
}

// C_STORE_RSP status codes.
// P3.4 GG4-1
const (
	CStoreStatusOutOfResources              uint16 = 0xa700
	CStoreStatusDataSetDoesNotMatchSOPClass uint16 = 0xa900
	CStoreStatusCannotUnderstand            uint16 = 0xc000
)

// P3.7 C
func decodeC_STORE_RSP(elems []*dicom.DicomElement) (*C_STORE_RSP, error) {
	v := &C_STORE_RSP{}
	var err error
	v.AffectedSOPClassUID, err = getStringFromElements(elems, dicom.TagAffectedSOPClassUID)
	if err != nil {
		return nil, err
	}
	v.MessageIDBeingRespondedTo, err = getUInt16FromElements(elems, dicom.TagMessageIDBeingRespondedTo)
	if err != nil {
		return nil, err
	}
	v.Status, err = getUInt16FromElements(elems, dicom.TagStatus)
	if err != nil {
		return nil, err
	}
	v.CommandDataSetType, err = getUInt16FromElements(elems, dicom.TagCommandDataSetType)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (v *C_STORE_RSP) Encode(e *dicom.Encoder) {
	doassert(v.CommandDataSetType == 0x101)
	encodeDataElementWithSingleValue(e, dicom.TagCommandField, uint16(0x8001))
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPClassUID, v.AffectedSOPClassUID)
	encodeDataElementWithSingleValue(e, dicom.TagMessageIDBeingRespondedTo, v.MessageIDBeingRespondedTo)
	encodeDataElementWithSingleValue(e, dicom.TagCommandDataSetType, v.CommandDataSetType)
	encodeDataElementWithSingleValue(e, dicom.TagAffectedSOPInstanceUID, v.AffectedSOPInstanceUID)
	encodeDataElementWithSingleValue(e, dicom.TagStatus, v.Status)
}

func (v *C_STORE_RSP) HasData() bool {
	doassert(v.CommandDataSetType == CommandDataSetTypeNull) // TODO(saito)
	return v.CommandDataSetType != CommandDataSetTypeNull
}

func (v *C_STORE_RSP) String() string {
	return fmt.Sprintf("cstorersp{sopclass:%v messageid:%v cmddatasettype: %v sopinstance: %v status: 0x%v}",
		v.AffectedSOPClassUID, v.MessageIDBeingRespondedTo, v.CommandDataSetType, v.AffectedSOPInstanceUID,
		v.Status)
}

func ReadDIMSEMessage(io io.Reader, limit int64) (DIMSEMessage, error) {
	var elems []*dicom.DicomElement
	// Note: DIMSE elements are always implicit LE.
	//
	// TODO(saito) make sure that's the case. Where the ref?
	d := dicom.NewDecoder(io, limit, binary.LittleEndian, dicom.ImplicitVR)
	for d.Len() > 0 {
		elem := dicom.ReadDataElement(d)
		if d.Error() != nil {
			break
		}
		elems = append(elems, elem)
	}
	if err := d.Finish(); err != nil {
		return nil, err
	}

	commandField, err := getUInt16FromElements(elems, dicom.TagCommandField)
	if err != nil {
		return nil, err
	}
	switch commandField {
	case 1:
		return decodeC_STORE_RQ(elems)
	case 0x8001:
		return decodeC_STORE_RSP(elems)
	}
	log.Printf("Unknown DIMSE command 0x%x", commandField)
	return nil, err
}

func encodeDIMSEMessage(v DIMSEMessage) ([]byte, error) {
	// DIMSE messages are always encoded Implicit+LE. See P3.7 6.3.1.
	subEncoder := dicom.NewEncoder(binary.LittleEndian, dicom.ImplicitVR)
	v.Encode(subEncoder)
	bytes, err := subEncoder.Finish()
	if err != nil {
		return nil, err
	}

	e := dicom.NewEncoder(binary.LittleEndian, dicom.ImplicitVR)
	encodeDataElementWithSingleValue(e, dicom.TagCommandGroupLength, uint32(len(bytes)))
	e.WriteBytes(bytes)
	return e.Finish()
}

type dimseCommandAssembler struct {
	contextID      byte
	commandBytes   []byte
	command        DIMSEMessage
	dataBytes      []byte
	readAllCommand bool

	readAllData bool
}

func addPDataTF(a *dimseCommandAssembler, pdu *P_DATA_TF, contextManager *contextManager) (string, string, DIMSEMessage, []byte, error) {
	for _, item := range pdu.Items {
		if a.contextID == 0 {
			a.contextID = item.ContextID
		} else if a.contextID != item.ContextID {
			return "", "", nil, nil, fmt.Errorf("Mixed context: %d %d", a.contextID, item.ContextID)
		}
		if item.Command {
			a.commandBytes = append(a.commandBytes, item.Value...)
			if item.Last {
				doassert(!a.readAllCommand)
				a.readAllCommand = true
			}
		} else {
			a.dataBytes = append(a.dataBytes, item.Value...)
			if item.Last {
				doassert(!a.readAllData)
				a.readAllData = true
			}
		}
	}
	if !a.readAllCommand {
		return "", "", nil, nil, nil
	}
	if a.command == nil {
		var err error
		a.command, err = ReadDIMSEMessage(bytes.NewBuffer(a.commandBytes), int64(len(a.commandBytes)))
		if err != nil {
			return "", "", nil, nil, err
		}
	}
	if a.command.HasData() && !a.readAllData {
		return "", "", nil, nil, nil
	}
	context, err := contextManager.lookupByContextID(a.contextID)
	if err != nil {
		return "", "", nil, nil, err
	}
	command := a.command
	dataBytes := a.dataBytes
	log.Printf("Read all data for syntax %s, command [%v], data %d bytes, err%v",
		dicom.UIDString(context.abstractSyntaxUID),
		command.String(), len(a.dataBytes), err)
	*a = dimseCommandAssembler{}
	return context.abstractSyntaxUID, context.transferSyntaxUID, command, dataBytes, nil
	// TODO(saito) Verify that there's no unread items after the last command&data.
}
<|endoftext|>


<commit_before><commit_msg>represent 4 states using State<commit_after><|endoftext|>


<commit_before>package libcontainerd

import (
	"io"
	"strings"
	"syscall"
	"time"

	"github.com/Microsoft/hcsshim"
	"github.com/Sirupsen/logrus"
)

type container struct {
	containerCommon

	// Platform specific fields are below here. There are none presently on Windows.
	options []CreateOption

	// The ociSpec is required, as client.Create() needs a spec,
	// but can be called from the RestartManager context which does not
	// otherwise have access to the Spec
	ociSpec Spec

	manualStopRequested bool
	hcsContainer        hcsshim.Container
}

func (ctr *container) newProcess(friendlyName string) *process {
	return &process{
		processCommon: processCommon{
			containerID:  ctr.containerID,
			friendlyName: friendlyName,
			client:       ctr.client,
		},
	}
}

func (ctr *container) start() error {
	var err error
	isServicing := false

	for _, option := range ctr.options {
		if s, ok := option.(*ServicingOption); ok && s.IsServicing {
			isServicing = true
		}
	}

	// Start the container.  If this is a servicing container, this call will block
	// until the container is done with the servicing execution.
	logrus.Debugln("Starting container ", ctr.containerID)
	if err = ctr.hcsContainer.Start(); err != nil {
		logrus.Errorf("Failed to start container: %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed Start. %s", err)
		} else {
			logrus.Debugln("Cleaned up after failed Start by calling Terminate")
		}
		return err
	}

	// Note we always tell HCS to
	// create stdout as it's required regardless of '-i' or '-t' options, so that
	// docker can always grab the output through logs. We also tell HCS to always
	// create stdin, even if it's not used - it will be closed shortly. Stderr
	// is only created if it we're not -t.
	createProcessParms := &hcsshim.ProcessConfig{
		EmulateConsole:   ctr.ociSpec.Process.Terminal,
		WorkingDirectory: ctr.ociSpec.Process.Cwd,
		ConsoleSize:      ctr.ociSpec.Process.InitialConsoleSize,
		CreateStdInPipe:  !isServicing,
		CreateStdOutPipe: !isServicing,
		CreateStdErrPipe: !ctr.ociSpec.Process.Terminal && !isServicing,
	}

	// Configure the environment for the process
	createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
	createProcessParms.CommandLine = strings.Join(ctr.ociSpec.Process.Args, " ")

	// Start the command running in the container.
	hcsProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
	if err != nil {
		logrus.Errorf("CreateProcess() failed %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed CreateProcess. %s", err)
		} else {
			logrus.Debugln("Cleaned up after failed CreateProcess by calling Terminate")
		}
		return err
	}
	ctr.startedAt = time.Now()

	// Save the hcs Process and PID
	ctr.process.friendlyName = InitFriendlyName
	pid := hcsProcess.Pid()
	ctr.process.hcsProcess = hcsProcess

	// If this is a servicing container, wait on the process synchronously here and
	// immediately call shutdown/terminate when it returns.
	if isServicing {
		exitCode := ctr.waitProcessExitCode(&ctr.process)

		if exitCode != 0 {
			logrus.Warnf("Servicing container %s returned non-zero exit code %d", ctr.containerID, exitCode)
			return ctr.terminate()
		}

		return ctr.shutdown()
	}

	var stdout, stderr io.ReadCloser
	var stdin io.WriteCloser
	stdin, stdout, stderr, err = hcsProcess.Stdio()
	if err != nil {
		logrus.Errorf("failed to get stdio pipes: %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed Stdio. %s", err)
		}
		return err
	}

	iopipe := &IOPipe{Terminal: ctr.ociSpec.Process.Terminal}

	iopipe.Stdin = createStdInCloser(stdin, hcsProcess)

	// TEMP: Work around Windows BS/DEL behavior.
	iopipe.Stdin = fixStdinBackspaceBehavior(iopipe.Stdin, ctr.ociSpec.Platform.OSVersion, ctr.ociSpec.Process.Terminal)

	// Convert io.ReadClosers to io.Readers
	if stdout != nil {
		iopipe.Stdout = openReaderFromPipe(stdout)
	}
	if stderr != nil {
		iopipe.Stderr = openReaderFromPipe(stderr)
	}

	// Save the PID
	logrus.Debugf("Process started - PID %d", pid)
	ctr.systemPid = uint32(pid)

	// Spin up a go routine waiting for exit to handle cleanup
	go ctr.waitExit(&ctr.process, true)

	ctr.client.appendContainer(ctr)

	if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
		// OK to return the error here, as waitExit will handle tear-down in HCS
		return err
	}

	// Tell the docker engine that the container has started.
	si := StateInfo{
		CommonStateInfo: CommonStateInfo{
			State: StateStart,
			Pid:   ctr.systemPid, // Not sure this is needed? Double-check monitor.go in daemon BUGBUG @jhowardmsft
		}}
	return ctr.client.backend.StateChanged(ctr.containerID, si)

}

// waitProcessExitCode will wait for the given process to exit and return its error code.
func (ctr *container) waitProcessExitCode(process *process) int {
	// Block indefinitely for the process to exit.
	err := process.hcsProcess.Wait()
	if err != nil {
		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
			logrus.Warnf("Wait failed (container may have been killed): %s", err)
		}
		// Fall through here, do not return. This ensures we attempt to continue the
		// shutdown in HCS and tell the docker engine that the process/container
		// has exited to avoid a container being dropped on the floor.
	}

	exitCode, err := process.hcsProcess.ExitCode()
	if err != nil {
		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
			logrus.Warnf("Unable to get exit code from container %s", ctr.containerID)
		}
		// Since we got an error retrieving the exit code, make sure that the code we return
		// doesn't incorrectly indicate success.
		exitCode = -1

		// Fall through here, do not return. This ensures we attempt to continue the
		// shutdown in HCS and tell the docker engine that the process/container
		// has exited to avoid a container being dropped on the floor.
	}

	if err := process.hcsProcess.Close(); err != nil {
		logrus.Error(err)
	}

	return exitCode
}

// waitExit runs as a goroutine waiting for the process to exit. It's
// equivalent to (in the linux containerd world) where events come in for
// state change notifications from containerd.
func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error {
	logrus.Debugln("waitExit on pid", process.systemPid)

	exitCode := ctr.waitProcessExitCode(process)

	// Assume the container has exited
	si := StateInfo{
		CommonStateInfo: CommonStateInfo{
			State:     StateExit,
			ExitCode:  uint32(exitCode),
			Pid:       process.systemPid,
			ProcessID: process.friendlyName,
		},
		UpdatePending: false,
	}

	// But it could have been an exec'd process which exited
	if !isFirstProcessToStart {
		si.State = StateExitProcess
	} else {
		updatePending, err := ctr.hcsContainer.HasPendingUpdates()
		if err != nil {
			logrus.Warnf("HasPendingUpdates failed (container may have been killed): %s", err)
		} else {
			si.UpdatePending = updatePending
		}

		logrus.Debugf("Shutting down container %s", ctr.containerID)
		if err := ctr.shutdown(); err != nil {
			logrus.Debugf("Failed to shutdown container %s", ctr.containerID)
		} else {
			logrus.Debugf("Completed shutting down container %s", ctr.containerID)
		}
		if err := ctr.hcsContainer.Close(); err != nil {
			logrus.Error(err)
		}

		if !ctr.manualStopRequested && ctr.restartManager != nil {
			restart, wait, err := ctr.restartManager.ShouldRestart(uint32(exitCode), false, time.Since(ctr.startedAt))
			if err != nil {
				logrus.Error(err)
			} else if restart {
				si.State = StateRestart
				ctr.restarting = true
				go func() {
					err := <-wait
					ctr.restarting = false
					ctr.client.deleteContainer(ctr.friendlyName)
					if err != nil {
						si.State = StateExit
						if err := ctr.client.backend.StateChanged(ctr.containerID, si); err != nil {
							logrus.Error(err)
						}
						logrus.Error(err)
					} else {
						ctr.client.Create(ctr.containerID, ctr.ociSpec, ctr.options...)
					}
				}()
			}
		}

		// Remove process from list if we have exited
		// We need to do so here in case the Message Handler decides to restart it.
		if si.State == StateExit {
			ctr.client.deleteContainer(ctr.friendlyName)
		}
	}

	// Call into the backend to notify it of the state change.
	logrus.Debugf("waitExit() calling backend.StateChanged %v", si)
	if err := ctr.client.backend.StateChanged(ctr.containerID, si); err != nil {
		logrus.Error(err)
	}

	logrus.Debugln("waitExit() completed OK")
	return nil
}

func (ctr *container) shutdown() error {
	const shutdownTimeout = time.Minute * 5
	err := ctr.hcsContainer.Shutdown()
	if err == hcsshim.ErrVmcomputeOperationPending {
		// Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely.
		err = ctr.hcsContainer.WaitTimeout(shutdownTimeout)
	}

	if err != nil {
		logrus.Debugf("error shutting down container %s %v calling terminate", ctr.containerID, err)
		if err := ctr.terminate(); err != nil {
			return err
		}
		return err
	}

	return nil
}

func (ctr *container) terminate() error {
	const terminateTimeout = time.Minute * 5
	err := ctr.hcsContainer.Terminate()

	if err == hcsshim.ErrVmcomputeOperationPending {
		err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
	}

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Added stateinfo to WaitExit info to aid debugging daemon hangs<commit_after>package libcontainerd

import (
	"io"
	"strings"
	"syscall"
	"time"

	"github.com/Microsoft/hcsshim"
	"github.com/Sirupsen/logrus"
)

type container struct {
	containerCommon

	// Platform specific fields are below here. There are none presently on Windows.
	options []CreateOption

	// The ociSpec is required, as client.Create() needs a spec,
	// but can be called from the RestartManager context which does not
	// otherwise have access to the Spec
	ociSpec Spec

	manualStopRequested bool
	hcsContainer        hcsshim.Container
}

func (ctr *container) newProcess(friendlyName string) *process {
	return &process{
		processCommon: processCommon{
			containerID:  ctr.containerID,
			friendlyName: friendlyName,
			client:       ctr.client,
		},
	}
}

func (ctr *container) start() error {
	var err error
	isServicing := false

	for _, option := range ctr.options {
		if s, ok := option.(*ServicingOption); ok && s.IsServicing {
			isServicing = true
		}
	}

	// Start the container.  If this is a servicing container, this call will block
	// until the container is done with the servicing execution.
	logrus.Debugln("Starting container ", ctr.containerID)
	if err = ctr.hcsContainer.Start(); err != nil {
		logrus.Errorf("Failed to start container: %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed Start. %s", err)
		} else {
			logrus.Debugln("Cleaned up after failed Start by calling Terminate")
		}
		return err
	}

	// Note we always tell HCS to
	// create stdout as it's required regardless of '-i' or '-t' options, so that
	// docker can always grab the output through logs. We also tell HCS to always
	// create stdin, even if it's not used - it will be closed shortly. Stderr
	// is only created if it we're not -t.
	createProcessParms := &hcsshim.ProcessConfig{
		EmulateConsole:   ctr.ociSpec.Process.Terminal,
		WorkingDirectory: ctr.ociSpec.Process.Cwd,
		ConsoleSize:      ctr.ociSpec.Process.InitialConsoleSize,
		CreateStdInPipe:  !isServicing,
		CreateStdOutPipe: !isServicing,
		CreateStdErrPipe: !ctr.ociSpec.Process.Terminal && !isServicing,
	}

	// Configure the environment for the process
	createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
	createProcessParms.CommandLine = strings.Join(ctr.ociSpec.Process.Args, " ")

	// Start the command running in the container.
	hcsProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
	if err != nil {
		logrus.Errorf("CreateProcess() failed %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed CreateProcess. %s", err)
		} else {
			logrus.Debugln("Cleaned up after failed CreateProcess by calling Terminate")
		}
		return err
	}
	ctr.startedAt = time.Now()

	// Save the hcs Process and PID
	ctr.process.friendlyName = InitFriendlyName
	pid := hcsProcess.Pid()
	ctr.process.hcsProcess = hcsProcess

	// If this is a servicing container, wait on the process synchronously here and
	// immediately call shutdown/terminate when it returns.
	if isServicing {
		exitCode := ctr.waitProcessExitCode(&ctr.process)

		if exitCode != 0 {
			logrus.Warnf("Servicing container %s returned non-zero exit code %d", ctr.containerID, exitCode)
			return ctr.terminate()
		}

		return ctr.shutdown()
	}

	var stdout, stderr io.ReadCloser
	var stdin io.WriteCloser
	stdin, stdout, stderr, err = hcsProcess.Stdio()
	if err != nil {
		logrus.Errorf("failed to get stdio pipes: %s", err)
		if err := ctr.terminate(); err != nil {
			logrus.Errorf("Failed to cleanup after a failed Stdio. %s", err)
		}
		return err
	}

	iopipe := &IOPipe{Terminal: ctr.ociSpec.Process.Terminal}

	iopipe.Stdin = createStdInCloser(stdin, hcsProcess)

	// TEMP: Work around Windows BS/DEL behavior.
	iopipe.Stdin = fixStdinBackspaceBehavior(iopipe.Stdin, ctr.ociSpec.Platform.OSVersion, ctr.ociSpec.Process.Terminal)

	// Convert io.ReadClosers to io.Readers
	if stdout != nil {
		iopipe.Stdout = openReaderFromPipe(stdout)
	}
	if stderr != nil {
		iopipe.Stderr = openReaderFromPipe(stderr)
	}

	// Save the PID
	logrus.Debugf("Process started - PID %d", pid)
	ctr.systemPid = uint32(pid)

	// Spin up a go routine waiting for exit to handle cleanup
	go ctr.waitExit(&ctr.process, true)

	ctr.client.appendContainer(ctr)

	if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
		// OK to return the error here, as waitExit will handle tear-down in HCS
		return err
	}

	// Tell the docker engine that the container has started.
	si := StateInfo{
		CommonStateInfo: CommonStateInfo{
			State: StateStart,
			Pid:   ctr.systemPid, // Not sure this is needed? Double-check monitor.go in daemon BUGBUG @jhowardmsft
		}}
	return ctr.client.backend.StateChanged(ctr.containerID, si)

}

// waitProcessExitCode will wait for the given process to exit and return its error code.
func (ctr *container) waitProcessExitCode(process *process) int {
	// Block indefinitely for the process to exit.
	err := process.hcsProcess.Wait()
	if err != nil {
		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
			logrus.Warnf("Wait failed (container may have been killed): %s", err)
		}
		// Fall through here, do not return. This ensures we attempt to continue the
		// shutdown in HCS and tell the docker engine that the process/container
		// has exited to avoid a container being dropped on the floor.
	}

	exitCode, err := process.hcsProcess.ExitCode()
	if err != nil {
		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != syscall.ERROR_BROKEN_PIPE {
			logrus.Warnf("Unable to get exit code from container %s", ctr.containerID)
		}
		// Since we got an error retrieving the exit code, make sure that the code we return
		// doesn't incorrectly indicate success.
		exitCode = -1

		// Fall through here, do not return. This ensures we attempt to continue the
		// shutdown in HCS and tell the docker engine that the process/container
		// has exited to avoid a container being dropped on the floor.
	}

	if err := process.hcsProcess.Close(); err != nil {
		logrus.Error(err)
	}

	return exitCode
}

// waitExit runs as a goroutine waiting for the process to exit. It's
// equivalent to (in the linux containerd world) where events come in for
// state change notifications from containerd.
func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error {
	logrus.Debugln("waitExit on pid", process.systemPid)

	exitCode := ctr.waitProcessExitCode(process)

	// Assume the container has exited
	si := StateInfo{
		CommonStateInfo: CommonStateInfo{
			State:     StateExit,
			ExitCode:  uint32(exitCode),
			Pid:       process.systemPid,
			ProcessID: process.friendlyName,
		},
		UpdatePending: false,
	}

	// But it could have been an exec'd process which exited
	if !isFirstProcessToStart {
		si.State = StateExitProcess
	} else {
		updatePending, err := ctr.hcsContainer.HasPendingUpdates()
		if err != nil {
			logrus.Warnf("HasPendingUpdates failed (container may have been killed): %s", err)
		} else {
			si.UpdatePending = updatePending
		}

		logrus.Debugf("Shutting down container %s", ctr.containerID)
		if err := ctr.shutdown(); err != nil {
			logrus.Debugf("Failed to shutdown container %s", ctr.containerID)
		} else {
			logrus.Debugf("Completed shutting down container %s", ctr.containerID)
		}
		if err := ctr.hcsContainer.Close(); err != nil {
			logrus.Error(err)
		}

		if !ctr.manualStopRequested && ctr.restartManager != nil {
			restart, wait, err := ctr.restartManager.ShouldRestart(uint32(exitCode), false, time.Since(ctr.startedAt))
			if err != nil {
				logrus.Error(err)
			} else if restart {
				si.State = StateRestart
				ctr.restarting = true
				go func() {
					err := <-wait
					ctr.restarting = false
					ctr.client.deleteContainer(ctr.friendlyName)
					if err != nil {
						si.State = StateExit
						if err := ctr.client.backend.StateChanged(ctr.containerID, si); err != nil {
							logrus.Error(err)
						}
						logrus.Error(err)
					} else {
						ctr.client.Create(ctr.containerID, ctr.ociSpec, ctr.options...)
					}
				}()
			}
		}

		// Remove process from list if we have exited
		// We need to do so here in case the Message Handler decides to restart it.
		if si.State == StateExit {
			ctr.client.deleteContainer(ctr.friendlyName)
		}
	}

	// Call into the backend to notify it of the state change.
	logrus.Debugf("waitExit() calling backend.StateChanged %+v", si)
	if err := ctr.client.backend.StateChanged(ctr.containerID, si); err != nil {
		logrus.Error(err)
	}

	logrus.Debugf("waitExit() completed OK, %+v", si)
	return nil
}

func (ctr *container) shutdown() error {
	const shutdownTimeout = time.Minute * 5
	err := ctr.hcsContainer.Shutdown()
	if err == hcsshim.ErrVmcomputeOperationPending {
		// Explicit timeout to avoid a (remote) possibility that shutdown hangs indefinitely.
		err = ctr.hcsContainer.WaitTimeout(shutdownTimeout)
	}

	if err != nil {
		logrus.Debugf("error shutting down container %s %v calling terminate", ctr.containerID, err)
		if err := ctr.terminate(); err != nil {
			return err
		}
		return err
	}

	return nil
}

func (ctr *container) terminate() error {
	const terminateTimeout = time.Minute * 5
	err := ctr.hcsContainer.Terminate()

	if err == hcsshim.ErrVmcomputeOperationPending {
		err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
	}

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package cmdlineclient

import (
	"code.google.com/p/go.net/websocket"
	"distributed2048/centralserver"
	"distributed2048/lib2048"
	"distributed2048/util"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

type cclient struct {
	conn       *websocket.Conn
	game       lib2048.Game2048
	movelist   []lib2048.Direction
	quitchan   chan int
	stoplisten chan int
}

var LOGV = util.NewLogger(false, "CMDLINECLIENT", os.Stdout)
var LOGE = util.NewLogger(true, "CMDLINECLIENT", os.Stderr)

func NewCClient(cservAddr, gameServHostPort string, interval int) (Cclient, error) {
	if gameServHostPort == "" {
		// Get server addr from central server
		isReady := false
		hostport := ""
		for !isReady {
			resp, err := http.Get(cservAddr)
			if err != nil {
				LOGV.Println("Could not connect to central server.")
				return nil, err
			}
			data, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				LOGV.Println("Your mother phat")
				return nil, err
			}
			LOGV.Println("received data from cserv")
			unpacked := &centralserver.HttpReply{}
			err = json.Unmarshal(data, &unpacked)
			if err != nil {
				LOGV.Println("Your mother phat")
				return nil, err
			}
			isReady = unpacked.Status == "OK"
			if isReady {
				hostport = unpacked.Hostport
			}
			time.Sleep(250 * time.Millisecond)
		}
		gameServHostPort = hostport
	}

	// Connect to the server
	origin := "http://localhost/"
	url := "ws://" + gameServHostPort + "/abc"
	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		LOGV.Println("Could not open websocket connection to server")
		return nil, err
	}
	game := lib2048.NewGame2048()
	cc := &cclient{
		ws,
		game,
		make([]lib2048.Direction, 0),
		make(chan int),
		make(chan int),
	}
	// Fire the ticker
	ticker := time.NewTicker(time.Duration(interval) * time.Millisecond)
	go cc.tickHandler(ticker)
	go cc.Listen()
	return cc, nil
}

// Ticker Function
func (c *cclient) tickHandler(ticker *time.Ticker) {
	defer LOGV.Println("client has stopped ticking.")
	for {
		select {
		case <-ticker.C:
			length := len(c.movelist)
			if length > 0 {
				LOGV.Println("movelist is length " + strconv.Itoa(length))
				var translatedMove int
				switch c.movelist[length-1] {
				case lib2048.Up:
					translatedMove = 0
				case lib2048.Right:
					translatedMove = 1
				case lib2048.Down:
					translatedMove = 2
				case lib2048.Left:
					translatedMove = 3
				}
				move := util.ClientMove{translatedMove}
				websocket.JSON.Send(c.conn, move)
				c.movelist = c.movelist[0:0]
			}
		case <-c.quitchan:
			ticker.Stop()
			return
		}
	}
}

func (c *cclient) Close() {
	LOGV.Println("closing...")
	c.quitchan <- 1
	c.stoplisten <- 1
	c.conn.Close()
}

func (c *cclient) InputMove(move lib2048.Direction) {
	LOGV.Println("client has input move: " + strconv.Itoa(int(move)))
	c.movelist = append(c.movelist, move)
}

func (c *cclient) GetGameState() lib2048.Game2048 {
	return c.game
}

func (c *cclient) Listen() {
	LOGV.Println("Listening to messages from the server")
	defer LOGV.Println("Stopped listening to server")
	for {
		select {
		case <-c.stoplisten:
			return
		default:
			var data []byte
			newState := &util.Game2048State{}
			websocket.Message.Receive(c.conn, &data)
			err := json.Unmarshal(data, newState)
			if err != nil {
				LOGE.Println(err)
			}
			LOGV.Print("Trying to set the board to: ")
			LOGV.Print(newState.Grid)
			c.game.SetGrid(newState.Grid)
			c.game.SetScore(newState.Score)
		}
	}
}
<commit_msg>upgraded client<commit_after>package cmdlineclient

import (
	"code.google.com/p/go.net/websocket"
	"distributed2048/centralserver"
	"distributed2048/lib2048"
	"distributed2048/util"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

type cclient struct {
	conn       *websocket.Conn
	game       lib2048.Game2048
	movelist   []lib2048.Direction
	quitchan   chan int
	stophandler chan int
	stopsender chan int
	stopreceiver chan int
	moveQueue  chan util.ClientMove
	cserv string
}

var LOGV = util.NewLogger(false, "CMDLINECLIENT", os.Stdout)
var LOGE = util.NewLogger(true, "CMDLINECLIENT", os.Stderr)

func NewCClient(cservAddr string, gameServHostPort string, interval int) (Cclient, error) {
	ws, err := doConnect(cservAddr, gameServHostPort)
	if err != nil {
		return nil, err
	}
	game := lib2048.NewGame2048()
	cc := &cclient{
		ws,
		game,
		make([]lib2048.Direction, 0),
		make(chan int),
		make(chan int),
		make(chan int),
		make(chan int),
		make(chan util.ClientMove),
		cservAddr,
	}
	// Fire the ticker
	ticker := time.NewTicker(time.Duration(interval) * time.Millisecond)
	go cc.tickHandler(ticker)
	go cc.websocketHandler()
	return cc, nil
}

func doConnect(cservAddr string, gameServHostPort string) (*websocket.Conn, error) {
	if gameServHostPort == "" {
		// Get server addr from central server
		isReady := false
		hostport := ""
		for !isReady {
			resp, err := http.Get(cservAddr)
			if err != nil {
				LOGV.Println("Could not connect to central server.")
				return nil, err
			}
			data, err := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			if err != nil {
				LOGV.Println("Your mother phat")
				return nil, err
			}
			LOGV.Println("received data from cserv")
			unpacked := &centralserver.HttpReply{}
			err = json.Unmarshal(data, &unpacked)
			if err != nil {
				LOGV.Println("Your mother phat")
				return nil, err
			}
			isReady = unpacked.Status == "OK"
			if isReady {
				hostport = unpacked.Hostport
			}
			time.Sleep(250 * time.Millisecond)
		}
		gameServHostPort = hostport
	}

	// Connect to the server
	origin := "http://localhost/"
	url := "ws://" + gameServHostPort + "/abc"
	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		LOGV.Println("Could not open websocket connection to server")
		return nil, err
	} else {
		LOGE.Println("Connection has been established with server " + gameServHostPort)
	}

	return ws, nil
}

// Ticker Function
func (c *cclient) tickHandler(ticker *time.Ticker) {
	defer LOGV.Println("client has stopped ticking.")
	for {
		select {
		case <-ticker.C:
			length := len(c.movelist)
			if length > 0 {
				LOGV.Println("movelist is length " + strconv.Itoa(length))
				var translatedMove int
				switch c.movelist[length-1] {
				case lib2048.Up:
					translatedMove = 0
				case lib2048.Right:
					translatedMove = 1
				case lib2048.Down:
					translatedMove = 2
				case lib2048.Left:
					translatedMove = 3
				}
				move := util.ClientMove{translatedMove}
				c.moveQueue <- move
				c.movelist = c.movelist[0:0]
//				websocket.JSON.Send(c.conn, move)
			}
		case <-c.quitchan:
			ticker.Stop()
			close(c.quitchan)
			return
		}
	}
}

func (c *cclient) Close() {
	LOGV.Println("closing...")
	c.quitchan <- 1
	c.stophandler <- 1
	c.conn.Close()
}

func (c *cclient) InputMove(move lib2048.Direction) {
	LOGV.Println("client has input move: " + strconv.Itoa(int(move)))
	c.movelist = append(c.movelist, move)
}

func (c *cclient) GetGameState() lib2048.Game2048 {
	return c.game
}

func (c *cclient) sender() (chan<- util.ClientMove, chan error) {
	ch, errCh := make(chan util.ClientMove), make(chan error)
	go func() {
		for {
			select {
			case <-c.stopsender:
				close(ch)
				return
			default:
				s := <-ch
				if err := websocket.JSON.Send(c.conn, s); err != nil {
					errCh <- err
					close(ch)
					return
				}
			}
		}
	}()
	return ch, errCh
}

func (c *cclient) receiver() (<-chan []byte, chan error) {
	ch, errCh := make(chan []byte), make(chan error)
	go func() {
		for {
			select {
			case <-c.stopreceiver:
				close(ch)
				return
			default:
				var s []byte
				if err := websocket.Message.Receive(c.conn, &s); err != nil {
					errCh <- err
					close(ch)
					return
				}
				ch <- s
			}
		}
	}()
	return ch, errCh
}

func (c *cclient) websocketHandler() {
	send, sendErr := c.sender()
	recv, recvErr := c.receiver()
	for {
		select {
		case s := <-c.moveQueue:
			send <- s
		case s := <-recv:
			newState := &util.Game2048State{}
			err := json.Unmarshal(s, newState)
			if err != nil {
				LOGE.Println(err)
			}
			LOGV.Print("Trying to set the board to: ")
			LOGV.Print(newState.Grid)
			c.game.SetGrid(newState.Grid)
			c.game.SetScore(newState.Score)
		case err := <-sendErr:
			LOGE.Println("Communication error with server while sending move: " + err.Error())
			<-c.stopreceiver
			ws, err := doConnect(c.cserv, "")
			if err != nil {
				LOGE.Println("Unable to reconnect to server. Shutting down..")
				go c.Close()
			}
			c.conn = ws
			send, sendErr = c.sender()
			recv, recvErr = c.receiver()
		case err := <-recvErr:
			LOGE.Println("Communication error with server while receiving state")
			<-c.stopsender
			ws, err := doConnect(c.cserv, "")
			if err != nil {
				LOGE.Println("Unable to reconnect to server. Shutting down..")
				go c.Close()
			}
			c.conn = ws
			send, sendErr = c.sender()
			recv, recvErr = c.receiver()
		case <-c.stophandler:
			<-c.stopreceiver
			<-c.stopsender
			close(c.stopreceiver)
			close(c.stopsender)
			close(c.stophandler)
			return
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Add uuid and hostname to heka message<commit_after><|endoftext|>


<commit_before>package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "strconv"
    "strings"
    "time"

    "github.com/boltdb/bolt"
    "github.com/vaxx99/zload/ama"
    "github.com/vaxx99/zload/bcd"
    "github.com/vaxx99/zload/cnf"
)

type block []ama.Redrec

var cfg *cnf.Config

func opendb(path, name string, mod os.FileMode) *bolt.DB {
    db, err := bolt.Open(path+"/"+name, mod, nil)
    if err != nil {
	log.Fatal(err)
    }
    return db
}

func check(e error) {
    if e != nil {
	panic(e)
    }
}

func term(c *cnf.Config) {
    os.Mkdir(cfg.Path+"/bdb/"+cfg.Term, 0777)
    if time.Now().Format("20060102")[6:8] == "01" && time.Now().Format("150405")[0:2] == "06" {
	fmt.Println("current period:", time.Now().Format("200601"))
	s := `{"Path":"` + c.Path + `","Port":"` + c.Port + `","Term":"` + time.Now().Format("200601") + `"}`
	d := []byte(s)
	os.Mkdir(cfg.Path+"/bdb/"+time.Now().Format("200601"), 0777)
	err := ioutil.WriteFile("conf.json", d, 0644)
	check(err)
    }

}

func week(day string) string {
    var s string
    switch day {
    case "01", "02", "03", "04", "05", "06", "07":
	s = "week01"
    case "08", "09", "10", "11", "12", "13", "14":
	s = "week02"
    case "15", "16", "17", "18", "19", "20", "21":
	s = "week03"
    case "22", "23", "24", "25", "26", "27", "28", "29", "30", "31":
	s = "week04"
    }

    return s
}

func wize(db *bolt.DB) {
    t := time.Now()
    days := map[string]int{}
    bckn := map[string]string{}
    os.Chdir(cfg.Path + "/bdb/" + cfg.Term)
    f, _ := ioutil.ReadDir(".")
    for _, fn := range f {
	if fn.Name()[0:4] == "week" {
	    wb := opendb(cfg.Path+"/bdb/" + cfg.Term + "/", fn.Name(), 0600)
	    bn := bname(wb)
	    for _, buckn := range bn {
		bckn[buckn] = fn.Name()
		wb.View(func(tx *bolt.Tx) error {
		    // Assume bucket exists and has keys
		    b := tx.Bucket([]byte(buckn))
		    b.ForEach(func(k, v []byte) error {
			days["ALL"]++
			days[string(k)[0:6]]++
			days[string(k)[0:8]]++
			j := strings.Index(string(k), "Hi")
			days[string(k)[0:8]+"."+string(k)[j+3:j+7]]++
			if strings.Index(string(k), "Hi.0003") != -1 {
			    //days[string(k)[0:8]+".0001"]++
			} else {
			    days[string(k)[0:8]+".0001"]++
			}
			return nil
		    })
		    return nil
		})
	    }
	    wb.Close()
	}
    }
    db.Update(func(tx *bolt.Tx) error {
	bucket, err := tx.CreateBucketIfNotExists([]byte("size"))
	for k, v := range days {
	    kv := strconv.Itoa(v)
	    err = bucket.Put([]byte(k), []byte(kv))
	}
	return err
    })
    db.Update(func(tx *bolt.Tx) error {
	bckt, err := tx.CreateBucketIfNotExists([]byte("buck"))
	for k, v := range bckn {
	    err = bckt.Put([]byte(k), []byte(v))
	}
	return err
    })
    fmt.Println("size:", days["ALL"], time.Now().Format("15:04:05"), time.Now().Sub(t))
}

func main() {
    fmt.Println("loader:", time.Now().Format("02.01.2006 15:04:05"))
    cnf.LoadConfig()
    cfg = cnf.GetConfig()
    os.Chdir(cfg.Path)
    term(cfg)
    cnf.LoadConfig()
    cfg = cnf.GetConfig()
    os.Chdir(cfg.Path + "/tmp")
    st0 := opendb(cfg.Path+"/bdb/"+cfg.Term, "stat0.db", 0666)
    defer st0.Close()
    f, _ := ioutil.ReadDir(".")
    ds := false
    for _, fn := range f {
	if fget(fn.Name(), st0) != true {
	    var w1, w2, w3, w4 block
	    ds = true
	    t0 := time.Now()
	    mn, pr, rp := rama(fn.Name())
	    set("file", fn.Name(), mn[0:8], st0)
	    for _, v := range rp {
		if v.Id[0:6] == cfg.Term {
		    switch week(v.Id[6:8]) {
		    case "week01":
			w1 = append(w1, v)
		    case "week02":
			w2 = append(w2, v)
		    case "week03":
			w3 = append(w3, v)
		    case "week04":
			w4 = append(w4, v)
		    }
		}
	    }
	    switch {
	    case len(w1) > 0:
		wb1 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week1.db", 0666)
		rset(w1, wb1)
		wb1.Close()
	    case len(w2) > 0:
		wb2 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week2.db", 0666)
		rset(w2, wb2)
		wb2.Close()
	    case len(w3) > 0:
		wb3 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week3.db", 0666)
		rset(w3, wb3)
		wb3.Close()
	    case len(w4) > 0:
		wb4 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week4.db", 0666)
		rset(w4, wb4)
		wb4.Close()
	    }
	    t1 := time.Now().Sub(t0)
	    log.Println(fn.Name(), mn[0:8], pr, "load", t1)
	}
	os.Remove(fn.Name())
    }
    if ds {
	wize(st0)
    }
    fmt.Println("*")
}

func rama(fn string) (string, int, block) {
    var recs block
    sw, ft, mT, rc, j, file, e := bcd.Finfo(fn)
    yy := mT[0:4]
    bcd.Err(e)
    defer file.Close()
    fe := false

    i := 0

    if ft == "AMA" && fe != true {

	for {
	    data, e := bcd.Read(file, j)
	    j = bcd.Next(data[len(data)-3])
	    bcd.Err(e)
	    ad := bcd.H2bcd(data)

	    if ad[0:6] == "AA9020" {
		b := ama.AA(ad, yy, sw)
		recs = append(recs, b)
	    }
	    if ad[0:6] == "AA9021" {
		b := ama.AA(ad, yy, sw)
		recs = append(recs, b)
	    }
	    if ad[0:6] == "AA9025" {
		b := ama.AA(ad, yy, sw)
		recs = append(recs, b)
	    }
	    if ad[0:6] == "AA9026" {
		b := ama.AA(ad, yy, sw)
		recs = append(recs, b)
	    }
	    if j == 0 {
		//exit on "EOF"
		return mT, rc, recs
	    }
	    i++
	}
    }

    if ft == "IAD" && fe != true {

	i := 0
	for i < rc {
	    data, e := bcd.Read(file, 42)
	    bcd.Err(e)
	    ad := bcd.H2bcd(data)
	    if ad[0:6] == "AA0003" {
		b := ama.AA(ad, yy, sw)
		recs = append(recs, b)
	    } else {
		_, e = bcd.Read(file, 32)
		bcd.Err(e)
	    }
	    i++
	}
    }
    return mT, rc, recs
}

func rset(recs []ama.Redrec, db *bolt.DB) {
    err := db.Update(func(tx *bolt.Tx) error {

	for _, v := range recs {
	    bucket, err := tx.CreateBucketIfNotExists([]byte(v.Id[0:8]))
	    if err != nil {
		return err
	    }
	    key := v.Id + ".Sw." + v.Sw + ".Hi." + v.Hi + ".Na." + v.Na + ".Nb." + v.Nb + ".Ds." + v.Ds + ".De." + v.De +
		".Dr." + v.Dr + ".Ot." + v.Ot + ".It." + v.It + ".Du." + v.Du

	    err = bucket.Put([]byte(key), []byte(v.Id[0:6]))

	}
	return nil
    })
    if err != nil {
	log.Fatal(err)
    }
}

func dates(dt string) string {
    rd := ""
    if len(dt) > 0 {
	rd = dt[6:8] + "." + dt[4:6] + "." + dt[0:4] + " " + dt[8:10] + ":" + dt[10:12] + ":" + dt[12:14]
    }
    return rd
}

func rget(buck, key string, db *bolt.DB) {
    err := db.View(func(tx *bolt.Tx) error {
	bucket := tx.Bucket([]byte(buck))
	if bucket == nil {
	    return fmt.Errorf("Bucket %q not found!", buck)
	}

	val := bucket.Get([]byte(key))
	fmt.Println(string(val))
	return nil
    })

    if err != nil {
	log.Fatal(err)
    }
}

func fget(key string, db *bolt.DB) bool {
    var f bool
    err := db.View(func(tx *bolt.Tx) error {
	bucket := tx.Bucket([]byte("file"))
	if bucket == nil {
	    f = false
	    return nil
	}

	val := bucket.Get([]byte(key))
	if val != nil {
	    f = true
	} else {
	    f = false
	}
	return nil
    })

    if err != nil {
	log.Fatal(err)
    }
    return f
}

func set(buck, key, val string, db *bolt.DB) {
    err := db.Update(func(tx *bolt.Tx) error {
	bucket, err := tx.CreateBucketIfNotExists([]byte(buck))
	if err != nil {
	    return err
	}

	err = bucket.Put([]byte(key), []byte(val))
	if err != nil {
	    return err
	}
	return nil
    })

    if err != nil {
	log.Fatal(err)
    }
}

func bname(db *bolt.DB) []string {
    var bn []string
    db.View(func(tx *bolt.Tx) error {
	c := tx.Cursor()
	for k, _ := c.First(); k != nil; k, _ = c.Next() {
	    if string(k)[0:4] != "file" && string(k)[0:4] != "size" {
		bn = append(bn, string(k))
	    }
	}
	return nil
    })
    return bn
}
<commit_msg>Update 09.08.2016<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/vaxx99/zload/ama"
	"github.com/vaxx99/zload/bcd"
	"github.com/vaxx99/zload/cnf"
)

type block []ama.Redrec

var cfg *cnf.Config

func opendb(path, name string, mod os.FileMode) *bolt.DB {
	db, err := bolt.Open(path+"/"+name, mod, nil)
	if err != nil {
		log.Fatal(err)
	}
	return db
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func term(c *cnf.Config) {
	os.Mkdir(cfg.Path+"/bdb/"+cfg.Term, 0777)
	if time.Now().Format("20060102")[6:8] == "01" && time.Now().Format("150405")[0:2] == "06" {
		fmt.Println("current period:", time.Now().Format("200601"))
		s := `{"Path":"` + c.Path + `","Port":"` + c.Port + `","Term":"` + time.Now().Format("200601") + `"}`
		d := []byte(s)
		os.Mkdir(cfg.Path+"/bdb/"+time.Now().Format("200601"), 0777)
		err := ioutil.WriteFile("conf.json", d, 0644)
		check(err)
	}

}

func week(day string) string {
	var s string
	switch day {
	case "01", "02", "03", "04", "05", "06", "07":
		s = "week01"
	case "08", "09", "10", "11", "12", "13", "14":
		s = "week02"
	case "15", "16", "17", "18", "19", "20", "21":
		s = "week03"
	case "22", "23", "24", "25", "26", "27", "28", "29", "30", "31":
		s = "week04"
	}

	return s
}

func wize(db *bolt.DB) {
	t := time.Now()
	days := map[string]int{}
	bckn := map[string]string{}
	os.Chdir(cfg.Path + "/bdb/" + cfg.Term)
	f, _ := ioutil.ReadDir(".")
	for _, fn := range f {
		if fn.Name()[0:4] == "week" {
			wb := opendb(cfg.Path+"/bdb/"+cfg.Term+"/", fn.Name(), 0600)
			bn := bname(wb)
			for _, buckn := range bn {
				bckn[buckn] = fn.Name()
				wb.View(func(tx *bolt.Tx) error {
					// Assume bucket exists and has keys
					b := tx.Bucket([]byte(buckn))
					b.ForEach(func(k, v []byte) error {
						days["ALL"]++
						days[string(k)[0:6]]++
						days[string(k)[0:8]]++
						j := strings.Index(string(k), "Hi")
						days[string(k)[0:8]+"."+string(k)[j+3:j+7]]++
						if strings.Index(string(k), "Hi.0003") != -1 {
							//days[string(k)[0:8]+".0001"]++
						} else {
							days[string(k)[0:8]+".0001"]++
						}
						return nil
					})
					return nil
				})
			}
			wb.Close()
		}
	}
	db.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists([]byte("size"))
		for k, v := range days {
			kv := strconv.Itoa(v)
			err = bucket.Put([]byte(k), []byte(kv))
		}
		return err
	})
	db.Update(func(tx *bolt.Tx) error {
		bckt, err := tx.CreateBucketIfNotExists([]byte("buck"))
		for k, v := range bckn {
			err = bckt.Put([]byte(k), []byte(v))
		}
		return err
	})
	fmt.Println("size:", days["ALL"], time.Now().Format("15:04:05"), time.Now().Sub(t))
}

func main() {
	fmt.Println("loader:", time.Now().Format("02.01.2006 15:04:05"))
	cnf.LoadConfig()
	cfg = cnf.GetConfig()
	os.Chdir(cfg.Path)
	term(cfg)
	cnf.LoadConfig()
	cfg = cnf.GetConfig()
	os.Chdir(cfg.Path + "/tmp")
	st0 := opendb(cfg.Path+"/bdb/"+cfg.Term, "stat0.db", 0666)
	defer st0.Close()
	f, _ := ioutil.ReadDir(".")
	ds := false
	for _, fn := range f {
		if fget(fn.Name(), st0) != true {
			var w1, w2, w3, w4 block
			ds = true
			t0 := time.Now()
			mn, pr, rp := rama(fn.Name())
			set("file", fn.Name(), mn[0:8], st0)
			for _, v := range rp {
				if v.Id[0:6] == cfg.Term {
					switch week(v.Id[6:8]) {
					case "week01":
						w1 = append(w1, v)
					case "week02":
						w2 = append(w2, v)
					case "week03":
						w3 = append(w3, v)
					case "week04":
						w4 = append(w4, v)
					}
				}
			}
	    if len(w1) > 0 {
		wb1 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week1.db", 0666)
		rset(w1, wb1)
		wb1.Close()
	    }
	    if len(w2) > 0 {
		wb2 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week2.db", 0666)
		rset(w2, wb2)
		wb2.Close()
	    }
	    if len(w3) > 0 {
		wb3 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week3.db", 0666)
		rset(w3, wb3)
		wb3.Close()
	    }
	    if len(w4) > 0 {
		wb4 := opendb(cfg.Path+"/bdb/"+cfg.Term, "week4.db", 0666)
		rset(w4, wb4)
		wb4.Close()
	    }
			t1 := time.Now().Sub(t0)
			log.Println(fn.Name(), mn[0:8], pr, "load", t1)
		}
		os.Remove(fn.Name())
	}
	if ds {
		wize(st0)
	}
	fmt.Println("*")
}

func rama(fn string) (string, int, block) {
	var recs block
	sw, ft, mT, rc, j, file, e := bcd.Finfo(fn)
	yy := mT[0:4]
	bcd.Err(e)
	defer file.Close()
	fe := false

	i := 0

	if ft == "AMA" && fe != true {

		for {
			data, e := bcd.Read(file, j)
			j = bcd.Next(data[len(data)-3])
			bcd.Err(e)
			ad := bcd.H2bcd(data)

			if ad[0:6] == "AA9020" {
				b := ama.AA(ad, yy, sw)
				recs = append(recs, b)
			}
			if ad[0:6] == "AA9021" {
				b := ama.AA(ad, yy, sw)
				recs = append(recs, b)
			}
			if ad[0:6] == "AA9025" {
				b := ama.AA(ad, yy, sw)
				recs = append(recs, b)
			}
			if ad[0:6] == "AA9026" {
				b := ama.AA(ad, yy, sw)
				recs = append(recs, b)
			}
			if j == 0 {
				//exit on "EOF"
				return mT, rc, recs
			}
			i++
		}
	}

	if ft == "IAD" && fe != true {

		i := 0
		for i < rc {
			data, e := bcd.Read(file, 42)
			bcd.Err(e)
			ad := bcd.H2bcd(data)
			if ad[0:6] == "AA0003" {
				b := ama.AA(ad, yy, sw)
				recs = append(recs, b)
			} else {
				_, e = bcd.Read(file, 32)
				bcd.Err(e)
			}
			i++
		}
	}
	return mT, rc, recs
}

func rset(recs []ama.Redrec, db *bolt.DB) {
	err := db.Update(func(tx *bolt.Tx) error {

		for _, v := range recs {
			bucket, err := tx.CreateBucketIfNotExists([]byte(v.Id[0:8]))
			if err != nil {
				return err
			}
			key := v.Id + ".Sw." + v.Sw + ".Hi." + v.Hi + ".Na." + v.Na + ".Nb." + v.Nb + ".Ds." + v.Ds + ".De." + v.De +
				".Dr." + v.Dr + ".Ot." + v.Ot + ".It." + v.It + ".Du." + v.Du

			err = bucket.Put([]byte(key), []byte(v.Id[0:6]))

		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
}

func dates(dt string) string {
	rd := ""
	if len(dt) > 0 {
		rd = dt[6:8] + "." + dt[4:6] + "." + dt[0:4] + " " + dt[8:10] + ":" + dt[10:12] + ":" + dt[12:14]
	}
	return rd
}

func rget(buck, key string, db *bolt.DB) {
	err := db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(buck))
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", buck)
		}

		val := bucket.Get([]byte(key))
		fmt.Println(string(val))
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
}

func fget(key string, db *bolt.DB) bool {
	var f bool
	err := db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte("file"))
		if bucket == nil {
			f = false
			return nil
		}

		val := bucket.Get([]byte(key))
		if val != nil {
			f = true
		} else {
			f = false
		}
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
	return f
}

func set(buck, key, val string, db *bolt.DB) {
	err := db.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists([]byte(buck))
		if err != nil {
			return err
		}

		err = bucket.Put([]byte(key), []byte(val))
		if err != nil {
			return err
		}
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}
}

func bname(db *bolt.DB) []string {
	var bn []string
	db.View(func(tx *bolt.Tx) error {
		c := tx.Cursor()
		for k, _ := c.First(); k != nil; k, _ = c.Next() {
			if string(k)[0:4] != "file" && string(k)[0:4] != "size" {
				bn = append(bn, string(k))
			}
		}
		return nil
	})
	return bn
}
<|endoftext|>


<commit_before><commit_msg>Make the address to listen on a flag.<commit_after><|endoftext|>


<commit_before>package nisttime

import (
	"fmt"
	"testing"
)

func TestTimeNowUTC(t *testing.T) {
	time, err := TimeNowUTC()
	if err != nil {
		t.Error(err)
	}

	if time == "" {
		t.Error("Server returned no data")
	}

	fmt.Println("time is => ", time)
}
<commit_msg>"Changed test to reflect changest in TimeNowUTC function, addtion of time struct to return<commit_after>package nisttime

import (
	"fmt"
	"testing"
)

func TestTimeNowUTC(t *testing.T) {
	tm, s, err := TimeNowUTC()
	if err != nil {
		t.Error(err)
	}

	if s == "" {
		t.Error("Server returned no data")
	}

	fmt.Println("time is => ", tm)
}
<|endoftext|>


<commit_before>// Copyright © 2016 davey mcglade  <davey.mcglade@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"

	"github.com/daveym/lint/lintapi"
	"github.com/spf13/cobra"
)

// ConsumerKey - Private key used to access Pocket API.
var ConsumerKey = ""

// AccessToken - Access token  used to access Pocket API (per request)
var AccessToken = ""

var authenticateCmd = &cobra.Command{
	Use:   "auth",
	Short: "Authenticate against Pocket using your consumer key. One off activity.",
	Long: `To access the Pocket API, you need to authenticate with your consumer key. Your consumer key
	can be found under the development area within the pocket website`,
	Run: func(cmd *cobra.Command, args []string) {

		//ConsumerKey = viper.Get("consumerkey")

		if len(args) > 0 {

			ConsumerKey = args[0]
			requestToken, err := Lint.Authenticate(ConsumerKey)

			if err != nil {
				fmt.Println("Please check your consumer key, it does not appear to be valid.")
			} else {

				exec.Command("open", Lint.UserAuthorisationURL+"request_token="+requestToken+"&redirect_uri="+Lint.RedirectURI)

				fmt.Println("")
				fmt.Println("Please authorise Lint from within your browser, or alternatively copy and paste the following link if the authentication page has not been displayed.")
				fmt.Println(Lint.UserAuthorisationURL + "request_token=" + requestToken + "&redirect_uri=" + Lint.RedirectURI)
				fmt.Println("")
				fmt.Println("and press ENTER when you have authorised the application to use Lint.")
				bufio.NewReader(os.Stdin).ReadBytes('\n')

				AccessToken, _, err := Lint.Authorise(ConsumerKey, requestToken)

				if err != nil {
					fmt.Println("Error authorising your consumer key and request token")
				} else {
					println(AccessToken)
				}

			}

		} else {
			fmt.Println("Consumer Key missing - please obtain this from the Pocket Developer site.")
		}
	}}

func init() {
	RootCmd.AddCommand(authenticateCmd)
}
<commit_msg>Open browser working<commit_after>// Copyright © 2016 davey mcglade  <davey.mcglade@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"

	"github.com/daveym/lint/lintapi"
	"github.com/spf13/cobra"
)

// ConsumerKey - Private key used to access Pocket API.
var ConsumerKey = ""

// AccessToken - Access token  used to access Pocket API (per request)
var AccessToken = ""

var authenticateCmd = &cobra.Command{
	Use:   "auth",
	Short: "Authenticate against Pocket using your consumer key. One off activity.",
	Long: `To access the Pocket API, you need to authenticate with your consumer key. Your consumer key
	can be found under the development area within the pocket website`,
	Run: func(cmd *cobra.Command, args []string) {

		//ConsumerKey = viper.Get("consumerkey")

		if len(args) > 0 {

			ConsumerKey = args[0]
			requestToken, err := Lint.Authenticate(ConsumerKey)

			if err != nil {
				fmt.Println("Please check your consumer key, it does not appear to be valid.")
			} else {

				cmd := exec.Command("open", Lint.UserAuthorisationURL+"request_token="+requestToken+"&redirect_uri="+Lint.RedirectURI)
				_, err := cmd.Output()

				if err != nil {
					println(err.Error())
					return
				}

				fmt.Println("")
				fmt.Println("Please authorise Lint from within your browser, or alternatively copy and paste the following link if the authentication page has not been displayed.")
				fmt.Println(Lint.UserAuthorisationURL + "request_token=" + requestToken + "&redirect_uri=" + Lint.RedirectURI)
				fmt.Println("")
				fmt.Println("and press ENTER when you have authorised the application to use Lint.")
				bufio.NewReader(os.Stdin).ReadBytes('\n')

				AccessToken, _, err := Lint.Authorise(ConsumerKey, requestToken)

				if err != nil {
					fmt.Println("Error authorising your consumer key and request token")
				} else {
					println(AccessToken)
				}

			}

		} else {
			fmt.Println("Consumer Key missing - please obtain this from the Pocket Developer site.")
		}
	}}

func init() {
	RootCmd.AddCommand(authenticateCmd)
}
<|endoftext|>