<commit_before>// Copyright 2013-2015 go-diameter authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package diam

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"sync"
	"time"

	"github.com/fiorix/go-diameter/diam/avp"
	"github.com/fiorix/go-diameter/diam/datatype"
	"github.com/fiorix/go-diameter/diam/dict"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// MessageBufferLength is the default buffer length for Diameter messages.
var MessageBufferLength = 1 << 10

// Message represents a Diameter message.
type Message struct {
	Header *Header
	AVP    []*AVP // AVPs in this message.

	// dictionary parser object used to encode and decode AVPs.
	dictionary *dict.Parser
}

// ReadMessage returns a Message. It uses the dictionary to parse the
// binary stream from the reader.
func ReadMessage(reader io.Reader, dictionary *dict.Parser) (*Message, error) {
	buf := newReaderBuffer()
	defer putReaderBuffer(buf)
	m := &Message{dictionary: dictionary}
	cmd, err := readAndParseHeader(reader, buf, m)
	if err != nil {
		return nil, err
	}
	if err = readAndParseBody(reader, buf, cmd, m); err != nil {
		return nil, err
	}
	return m, nil
}

var readerBufferPool sync.Pool

func newReaderBuffer() *bytes.Buffer {
	if v := readerBufferPool.Get(); v != nil {
		return v.(*bytes.Buffer)
	}
	return bytes.NewBuffer(make([]byte, MessageBufferLength))
}

func putReaderBuffer(b *bytes.Buffer) {
	if cap(b.Bytes()) == MessageBufferLength {
		b.Reset()
		readerBufferPool.Put(b)
	}
}

func readerBufferSlice(b *bytes.Buffer, l int) []byte {
	p := b.Bytes()
	if l <= MessageBufferLength && cap(p) >= MessageBufferLength {
		return p[0:l]
	}
	return make([]byte, l)
}

func readAndParseHeader(r io.Reader, buf *bytes.Buffer, m *Message) (cmd *dict.Command, err error) {
	b := buf.Bytes()[:HeaderLength]
	if _, err = io.ReadFull(r, b); err != nil {
		return nil, io.ErrUnexpectedEOF
	}
	m.Header, err = DecodeHeader(b)
	if err != nil {
		return nil, err
	}
	cmd, err = m.Dictionary().FindCommand(
		m.Header.ApplicationID,
		m.Header.CommandCode,
	)
	if err != nil {
		return nil, err
	}
	return cmd, nil
}

func readAndParseBody(r io.Reader, buf *bytes.Buffer, cmd *dict.Command, m *Message) error {
	b := readerBufferSlice(buf, int(m.Header.MessageLength-HeaderLength))
	_, err := io.ReadFull(r, b)
	if err != nil {
		return err
	}
	n := maxAVPs(m, cmd)
	if n == 0 {
		// TODO: fail to load the dictionary instead.
		return fmt.Errorf(
			"Command %s (%d) has no AVPs defined in the dictionary.",
			cmd.Name, cmd.Code)
	}
	// Pre-allocate max # of AVPs for this message.
	m.AVP = make([]*AVP, 0, n)
	if err = decodeAVPs(m, b); err != nil {
		return err
	}
	return nil
}

func maxAVPs(m *Message, cmd *dict.Command) int {
	if m.Header.CommandFlags&RequestFlag == RequestFlag {
		return len(cmd.Request.Rule)
	}
	return len(cmd.Answer.Rule)
}

func decodeAVPs(m *Message, pbytes []byte) error {
	var a *AVP
	var err error
	for n := 0; n < len(pbytes); {
		a, err = DecodeAVP(pbytes[n:],
			m.Header.ApplicationID, m.Dictionary())
		if err != nil {
			return fmt.Errorf("Failed to decode AVP: %s", err)
		}
		m.AVP = append(m.AVP, a)
		n += a.Len()
	}
	return nil
}

// NewMessage creates and initializes a Message.
func NewMessage(cmd uint32, flags uint8, appid, hopbyhop, endtoend uint32, dictionary *dict.Parser) *Message {
	if hopbyhop == 0 {
		hopbyhop = rand.Uint32()
	}
	if endtoend == 0 {
		endtoend = rand.Uint32()
	}
	return &Message{
		Header: &Header{
			Version:       1,
			MessageLength: HeaderLength,
			CommandFlags:  flags,
			CommandCode:   cmd,
			ApplicationID: appid,
			HopByHopID:    hopbyhop,
			EndToEndID:    endtoend,
		},
		dictionary: dictionary,
	}
}

// NewRequest creates a new Message with the Request bit set.
func NewRequest(cmd uint32, appid uint32, dictionary *dict.Parser) *Message {
	return NewMessage(cmd, RequestFlag, appid, 0, 0, dictionary)
}

// Dictionary returns the dictionary parser object associated with this
// message. This dictionary is used to encode and decode the message.
// If no dictionary is associated then it returns the default dictionary.
func (m *Message) Dictionary() *dict.Parser {
	if m.dictionary == nil {
		return dict.Default
	}
	return m.dictionary
}

// NewAVP creates and initializes a new AVP and adds it to the Message.
// It is not safe for concurrent calls.
func (m *Message) NewAVP(code interface{}, flags uint8, vendor uint32, data datatype.Type) (*AVP, error) {
	var a *AVP
	switch code.(type) {
	case int:
		a = NewAVP(uint32(code.(int)), flags, vendor, data)
	case uint32:
		a = NewAVP(code.(uint32), flags, vendor, data)
	case string:
		dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			code.(string),
		)
		if err != nil {
			return nil, err
		}
		a = NewAVP(dictAVP.Code, flags, vendor, data)
	}
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
	return a, nil
}

// AddAVP adds the AVP to the Message. It is not safe for concurrent calls.
func (m *Message) AddAVP(a *AVP) {
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
}

// InsertAVP inserts the AVP to the Message as the first AVP. It is not
// safe for concurrent calls.
func (m *Message) InsertAVP(a *AVP) {
	m.AVP = append([]*AVP{a}, m.AVP...)
	m.Header.MessageLength += uint32(a.Len())
}

// WriteTo serializes the Message and writes into the writer.
func (m *Message) WriteTo(writer io.Writer) (int64, error) {
	l := m.Len()
	buf := newWriterBuffer(l)
	defer putWriterBuffer(buf)
	b := buf.Bytes()[0:l]
	if err := m.SerializeTo(b); err != nil {
		return 0, err
	}
	n, err := writer.Write(b)
	if err != nil {
		return 0, err
	}
	return int64(n), err
}

var writerBufferPool sync.Pool

func newWriterBuffer(min int) *bytes.Buffer {
	if min > MessageBufferLength {
		return bytes.NewBuffer(make([]byte, min))
	}
	if v := writerBufferPool.Get(); v != nil {
		return v.(*bytes.Buffer)
	}
	return bytes.NewBuffer(make([]byte, MessageBufferLength))
}

func putWriterBuffer(b *bytes.Buffer) {
	b.Reset()
	if cap(b.Bytes()) == MessageBufferLength {
		writerBufferPool.Put(b)
	}
}

// Serialize returns the serialized bytes of the Message.
func (m *Message) Serialize() ([]byte, error) {
	b := make([]byte, m.Len())
	if err := m.SerializeTo(b); err != nil {
		return nil, err
	}
	return b, nil
}

// SerializeTo writes the serialized bytes of the Message into b.
func (m *Message) SerializeTo(b []byte) (err error) {
	m.Header.SerializeTo(b[0:HeaderLength])
	offset := HeaderLength
	for _, avp := range m.AVP {
		if err = avp.SerializeTo(b[offset:]); err != nil {
			return err
		}
		offset += avp.Len()
	}
	return nil
}

// Len returns the length of the Message in bytes.
func (m *Message) Len() int {
	l := HeaderLength
	for _, avp := range m.AVP {
		l += avp.Len()
	}
	return l
}

func findFromAVP(avps []*AVP, code uint32, findMultiple bool) ([]*AVP, error) {

	avpResult := make([]*AVP, 0)

	for _, a := range avps {

		if a.Code == code {
			avpResult = append(avpResult, a)
			if !findMultiple {
				return avpResult, nil
			}
		}

		if a.Data.Type() == GroupedAVPType {
			groupedAVP := a.Data
			result, err := findFromAVP(groupedAVP.(*GroupedAVP).AVP, code, findMultiple)
			if err == nil {
				avpResult = append(avpResult, result...)
				if !findMultiple {
					return avpResult, nil
				}
			}
		}
	}

	if len(avpResult) == 0 {
		return nil, errors.New("AVP not found")
	}

	return avpResult, nil
}

// FindAVP searches the Message for all avps that match the search criteria.
// The code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avps, err := m.FindAVPs(264)
//	avps, err := m.FindAVPs(avp.OriginHost)
//	avps, err := m.FindAVPs("Origin-Host")
//
func (m *Message) FindAVPs(code interface{}) ([]*AVP, error) {

	dictAVP, err := m.Dictionary().FindAVP(m.Header.ApplicationID, code)

	if err != nil {
		return nil, err
	}

	return findFromAVP(m.AVP, dictAVP.Code, true)
}

// FindAVP searches the Message for a specific AVP.
// The code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avp, err := m.FindAVP(264)
//	avp, err := m.FindAVP(avp.OriginHost)
//	avp, err := m.FindAVP("Origin-Host")
//
func (m *Message) FindAVP(code interface{}) (*AVP, error) {

	dictAVP, err := m.Dictionary().FindAVP(m.Header.ApplicationID, code)

	if err != nil {
		return nil, err
	}

	result, err := findFromAVP(m.AVP, dictAVP.Code, false)

	if err == nil {
		return result[0], err
	} else {
		return nil, err
	}
}

// Answer creates an answer for the current Message with an embedded
// Result-Code AVP.
func (m *Message) Answer(resultCode uint32) *Message {
	nm := NewMessage(
		m.Header.CommandCode,
		m.Header.CommandFlags&^RequestFlag, // Reset the Request bit.
		m.Header.ApplicationID,
		m.Header.HopByHopID,
		m.Header.EndToEndID,
		m.Dictionary(),
	)

	nm.NewAVP(avp.ResultCode, avp.Mbit, 0, datatype.Unsigned32(resultCode))
	return nm
}

func (m *Message) String() string {
	var b bytes.Buffer
	var typ string
	if m.Header.CommandFlags&RequestFlag == RequestFlag {
		typ = "Request"
	} else {
		typ = "Answer"
	}
	if dictCMD, err := m.Dictionary().FindCommand(
		m.Header.ApplicationID,
		m.Header.CommandCode,
	); err != nil {
		fmt.Fprintf(&b, "Unknown-%s\n%s\n", typ, m.Header)
	} else {
		fmt.Fprintf(&b, "%s-%s (%s%c)\n%s\n",
			dictCMD.Name,
			typ,
			dictCMD.Short,
			typ[0],
			m.Header,
		)
	}
	for _, a := range m.AVP {
		if dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			a.Code,
		); err != nil {
			fmt.Fprintf(&b, "\tUnknown %s (%s)\n", a, err)
		} else if a.Data.Type() == GroupedAVPType {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, printGrouped("\t", m, a, 1))
		} else {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, a)
		}
	}
	return b.String()
}

func printGrouped(prefix string, m *Message, a *AVP, indent int) string {
	var b bytes.Buffer
	fmt.Fprintf(&b, "{Code:%d,Flags:0x%x,Length:%d,VendorId:%d,Value:Grouped{\n",
		a.Code,
		a.Flags,
		a.Len(),
		a.VendorID,
	)
	for _, ga := range a.Data.(*GroupedAVP).AVP {
		if dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			ga.Code,
		); err != nil {
			fmt.Fprintf(&b, "%s\tUnknown %s (%s),\n", prefix, ga, err)
		} else {
			if ga.Data.Type() == GroupedAVPType {
				indent++
				tabs := indentTabs(indent)
				fmt.Fprintf(&b, "%s%s %s\n", tabs, dictAVP.Name, printGrouped(tabs, m, ga, indent))
			} else {
				fmt.Fprintf(&b, "%s\t%s %s,\n", prefix, dictAVP.Name, ga)
			}
		}
	}
	fmt.Fprintf(&b, "%s}}", prefix)
	return b.String()
}

func indentTabs(n int) string {
	var s string
	for i := 0; i < n; i++ {
		s += "\t"
	}
	return s
}
<commit_msg>Blank lines removed<commit_after>// Copyright 2013-2015 go-diameter authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package diam

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"sync"
	"time"

	"github.com/fiorix/go-diameter/diam/avp"
	"github.com/fiorix/go-diameter/diam/datatype"
	"github.com/fiorix/go-diameter/diam/dict"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// MessageBufferLength is the default buffer length for Diameter messages.
var MessageBufferLength = 1 << 10

// Message represents a Diameter message.
type Message struct {
	Header *Header
	AVP    []*AVP // AVPs in this message.

	// dictionary parser object used to encode and decode AVPs.
	dictionary *dict.Parser
}

// ReadMessage returns a Message. It uses the dictionary to parse the
// binary stream from the reader.
func ReadMessage(reader io.Reader, dictionary *dict.Parser) (*Message, error) {
	buf := newReaderBuffer()
	defer putReaderBuffer(buf)
	m := &Message{dictionary: dictionary}
	cmd, err := readAndParseHeader(reader, buf, m)
	if err != nil {
		return nil, err
	}
	if err = readAndParseBody(reader, buf, cmd, m); err != nil {
		return nil, err
	}
	return m, nil
}

var readerBufferPool sync.Pool

func newReaderBuffer() *bytes.Buffer {
	if v := readerBufferPool.Get(); v != nil {
		return v.(*bytes.Buffer)
	}
	return bytes.NewBuffer(make([]byte, MessageBufferLength))
}

func putReaderBuffer(b *bytes.Buffer) {
	if cap(b.Bytes()) == MessageBufferLength {
		b.Reset()
		readerBufferPool.Put(b)
	}
}

func readerBufferSlice(b *bytes.Buffer, l int) []byte {
	p := b.Bytes()
	if l <= MessageBufferLength && cap(p) >= MessageBufferLength {
		return p[0:l]
	}
	return make([]byte, l)
}

func readAndParseHeader(r io.Reader, buf *bytes.Buffer, m *Message) (cmd *dict.Command, err error) {
	b := buf.Bytes()[:HeaderLength]
	if _, err = io.ReadFull(r, b); err != nil {
		return nil, io.ErrUnexpectedEOF
	}
	m.Header, err = DecodeHeader(b)
	if err != nil {
		return nil, err
	}
	cmd, err = m.Dictionary().FindCommand(
		m.Header.ApplicationID,
		m.Header.CommandCode,
	)
	if err != nil {
		return nil, err
	}
	return cmd, nil
}

func readAndParseBody(r io.Reader, buf *bytes.Buffer, cmd *dict.Command, m *Message) error {
	b := readerBufferSlice(buf, int(m.Header.MessageLength-HeaderLength))
	_, err := io.ReadFull(r, b)
	if err != nil {
		return err
	}
	n := maxAVPs(m, cmd)
	if n == 0 {
		// TODO: fail to load the dictionary instead.
		return fmt.Errorf(
			"Command %s (%d) has no AVPs defined in the dictionary.",
			cmd.Name, cmd.Code)
	}
	// Pre-allocate max # of AVPs for this message.
	m.AVP = make([]*AVP, 0, n)
	if err = decodeAVPs(m, b); err != nil {
		return err
	}
	return nil
}

func maxAVPs(m *Message, cmd *dict.Command) int {
	if m.Header.CommandFlags&RequestFlag == RequestFlag {
		return len(cmd.Request.Rule)
	}
	return len(cmd.Answer.Rule)
}

func decodeAVPs(m *Message, pbytes []byte) error {
	var a *AVP
	var err error
	for n := 0; n < len(pbytes); {
		a, err = DecodeAVP(pbytes[n:],
			m.Header.ApplicationID, m.Dictionary())
		if err != nil {
			return fmt.Errorf("Failed to decode AVP: %s", err)
		}
		m.AVP = append(m.AVP, a)
		n += a.Len()
	}
	return nil
}

// NewMessage creates and initializes a Message.
func NewMessage(cmd uint32, flags uint8, appid, hopbyhop, endtoend uint32, dictionary *dict.Parser) *Message {
	if hopbyhop == 0 {
		hopbyhop = rand.Uint32()
	}
	if endtoend == 0 {
		endtoend = rand.Uint32()
	}
	return &Message{
		Header: &Header{
			Version:       1,
			MessageLength: HeaderLength,
			CommandFlags:  flags,
			CommandCode:   cmd,
			ApplicationID: appid,
			HopByHopID:    hopbyhop,
			EndToEndID:    endtoend,
		},
		dictionary: dictionary,
	}
}

// NewRequest creates a new Message with the Request bit set.
func NewRequest(cmd uint32, appid uint32, dictionary *dict.Parser) *Message {
	return NewMessage(cmd, RequestFlag, appid, 0, 0, dictionary)
}

// Dictionary returns the dictionary parser object associated with this
// message. This dictionary is used to encode and decode the message.
// If no dictionary is associated then it returns the default dictionary.
func (m *Message) Dictionary() *dict.Parser {
	if m.dictionary == nil {
		return dict.Default
	}
	return m.dictionary
}

// NewAVP creates and initializes a new AVP and adds it to the Message.
// It is not safe for concurrent calls.
func (m *Message) NewAVP(code interface{}, flags uint8, vendor uint32, data datatype.Type) (*AVP, error) {
	var a *AVP
	switch code.(type) {
	case int:
		a = NewAVP(uint32(code.(int)), flags, vendor, data)
	case uint32:
		a = NewAVP(code.(uint32), flags, vendor, data)
	case string:
		dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			code.(string),
		)
		if err != nil {
			return nil, err
		}
		a = NewAVP(dictAVP.Code, flags, vendor, data)
	}
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
	return a, nil
}

// AddAVP adds the AVP to the Message. It is not safe for concurrent calls.
func (m *Message) AddAVP(a *AVP) {
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
}

// InsertAVP inserts the AVP to the Message as the first AVP. It is not
// safe for concurrent calls.
func (m *Message) InsertAVP(a *AVP) {
	m.AVP = append([]*AVP{a}, m.AVP...)
	m.Header.MessageLength += uint32(a.Len())
}

// WriteTo serializes the Message and writes into the writer.
func (m *Message) WriteTo(writer io.Writer) (int64, error) {
	l := m.Len()
	buf := newWriterBuffer(l)
	defer putWriterBuffer(buf)
	b := buf.Bytes()[0:l]
	if err := m.SerializeTo(b); err != nil {
		return 0, err
	}
	n, err := writer.Write(b)
	if err != nil {
		return 0, err
	}
	return int64(n), err
}

var writerBufferPool sync.Pool

func newWriterBuffer(min int) *bytes.Buffer {
	if min > MessageBufferLength {
		return bytes.NewBuffer(make([]byte, min))
	}
	if v := writerBufferPool.Get(); v != nil {
		return v.(*bytes.Buffer)
	}
	return bytes.NewBuffer(make([]byte, MessageBufferLength))
}

func putWriterBuffer(b *bytes.Buffer) {
	b.Reset()
	if cap(b.Bytes()) == MessageBufferLength {
		writerBufferPool.Put(b)
	}
}

// Serialize returns the serialized bytes of the Message.
func (m *Message) Serialize() ([]byte, error) {
	b := make([]byte, m.Len())
	if err := m.SerializeTo(b); err != nil {
		return nil, err
	}
	return b, nil
}

// SerializeTo writes the serialized bytes of the Message into b.
func (m *Message) SerializeTo(b []byte) (err error) {
	m.Header.SerializeTo(b[0:HeaderLength])
	offset := HeaderLength
	for _, avp := range m.AVP {
		if err = avp.SerializeTo(b[offset:]); err != nil {
			return err
		}
		offset += avp.Len()
	}
	return nil
}

// Len returns the length of the Message in bytes.
func (m *Message) Len() int {
	l := HeaderLength
	for _, avp := range m.AVP {
		l += avp.Len()
	}
	return l
}

func findFromAVP(avps []*AVP, code uint32, findMultiple bool) ([]*AVP, error) {

	avpResult := make([]*AVP, 0)

	for _, a := range avps {

		if a.Code == code {
			avpResult = append(avpResult, a)
			if !findMultiple {
				return avpResult, nil
			}
		}

		if a.Data.Type() == GroupedAVPType {
			groupedAVP := a.Data
			result, err := findFromAVP(groupedAVP.(*GroupedAVP).AVP, code, findMultiple)
			if err == nil {
				avpResult = append(avpResult, result...)
				if !findMultiple {
					return avpResult, nil
				}
			}
		}
	}

	if len(avpResult) == 0 {
		return nil, errors.New("AVP not found")
	}

	return avpResult, nil
}

// FindAVP searches the Message for all avps that match the search criteria.
// The code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avps, err := m.FindAVPs(264)
//	avps, err := m.FindAVPs(avp.OriginHost)
//	avps, err := m.FindAVPs("Origin-Host")
//
func (m *Message) FindAVPs(code interface{}) ([]*AVP, error) {
	dictAVP, err := m.Dictionary().FindAVP(m.Header.ApplicationID, code)

	if err != nil {
		return nil, err
	}

	return findFromAVP(m.AVP, dictAVP.Code, true)
}

// FindAVP searches the Message for a specific AVP.
// The code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avp, err := m.FindAVP(264)
//	avp, err := m.FindAVP(avp.OriginHost)
//	avp, err := m.FindAVP("Origin-Host")
//
func (m *Message) FindAVP(code interface{}) (*AVP, error) {
	dictAVP, err := m.Dictionary().FindAVP(m.Header.ApplicationID, code)

	if err != nil {
		return nil, err
	}

	result, err := findFromAVP(m.AVP, dictAVP.Code, false)

	if err == nil {
		return result[0], err
	} else {
		return nil, err
	}
}

// Answer creates an answer for the current Message with an embedded
// Result-Code AVP.
func (m *Message) Answer(resultCode uint32) *Message {
	nm := NewMessage(
		m.Header.CommandCode,
		m.Header.CommandFlags&^RequestFlag, // Reset the Request bit.
		m.Header.ApplicationID,
		m.Header.HopByHopID,
		m.Header.EndToEndID,
		m.Dictionary(),
	)
	nm.NewAVP(avp.ResultCode, avp.Mbit, 0, datatype.Unsigned32(resultCode))
	return nm
}

func (m *Message) String() string {
	var b bytes.Buffer
	var typ string
	if m.Header.CommandFlags&RequestFlag == RequestFlag {
		typ = "Request"
	} else {
		typ = "Answer"
	}
	if dictCMD, err := m.Dictionary().FindCommand(
		m.Header.ApplicationID,
		m.Header.CommandCode,
	); err != nil {
		fmt.Fprintf(&b, "Unknown-%s\n%s\n", typ, m.Header)
	} else {
		fmt.Fprintf(&b, "%s-%s (%s%c)\n%s\n",
			dictCMD.Name,
			typ,
			dictCMD.Short,
			typ[0],
			m.Header,
		)
	}
	for _, a := range m.AVP {
		if dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			a.Code,
		); err != nil {
			fmt.Fprintf(&b, "\tUnknown %s (%s)\n", a, err)
		} else if a.Data.Type() == GroupedAVPType {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, printGrouped("\t", m, a, 1))
		} else {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, a)
		}
	}
	return b.String()
}

func printGrouped(prefix string, m *Message, a *AVP, indent int) string {
	var b bytes.Buffer
	fmt.Fprintf(&b, "{Code:%d,Flags:0x%x,Length:%d,VendorId:%d,Value:Grouped{\n",
		a.Code,
		a.Flags,
		a.Len(),
		a.VendorID,
	)
	for _, ga := range a.Data.(*GroupedAVP).AVP {
		if dictAVP, err := m.Dictionary().FindAVP(
			m.Header.ApplicationID,
			ga.Code,
		); err != nil {
			fmt.Fprintf(&b, "%s\tUnknown %s (%s),\n", prefix, ga, err)
		} else {
			if ga.Data.Type() == GroupedAVPType {
				indent++
				tabs := indentTabs(indent)
				fmt.Fprintf(&b, "%s%s %s\n", tabs, dictAVP.Name, printGrouped(tabs, m, ga, indent))
			} else {
				fmt.Fprintf(&b, "%s\t%s %s,\n", prefix, dictAVP.Name, ga)
			}
		}
	}
	fmt.Fprintf(&b, "%s}}", prefix)
	return b.String()
}

func indentTabs(n int) string {
	var s string
	for i := 0; i < n; i++ {
		s += "\t"
	}
	return s
}
<|endoftext|>


<commit_before>package notifications

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"golang.org/x/net/context/ctxhttp"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/util"
)

type Webhook struct {
	Url        string
	User       string
	Password   string
	Body       string
	HttpMethod string
	HttpHeader map[string]string
}

var netTransport = &http.Transport{
	Dial: (&net.Dialer{
		Timeout: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 5 * time.Second,
}
var netClient = &http.Client{
	Timeout:   time.Second * 30,
	Transport: netTransport,
}

var (
	webhookQueue chan *Webhook
	webhookLog   log.Logger
)

func initWebhookQueue() {
	webhookLog = log.New("notifications.webhook")
	webhookQueue = make(chan *Webhook, 10)
	go processWebhookQueue()
}

func processWebhookQueue() {
	for {
		select {
		case webhook := <-webhookQueue:
			err := sendWebRequestSync(context.Background(), webhook)

			if err != nil {
				webhookLog.Error("Failed to send webrequest ", "error", err)
			}
		}
	}
}

func sendWebRequestSync(ctx context.Context, webhook *Webhook) error {
	webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod)

	if webhook.HttpMethod == "" {
		webhook.HttpMethod = http.MethodPost
	}

	request, err := http.NewRequest(webhook.HttpMethod, webhook.Url, bytes.NewReader([]byte(webhook.Body)))
	if err != nil {
		return err
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "Grafana")
	if webhook.User != "" && webhook.Password != "" {
		request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))
	}

	for k, v := range webhook.HttpHeader {
		request.Header.Set(k, v)
	}

	resp, err := ctxhttp.Do(ctx, netClient, request)
	if err != nil {
		return err
	}

	if resp.StatusCode/100 == 2 {
		return nil
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	webhookLog.Debug("Webhook failed", "statuscode", resp.Status, "body", string(body))
	return fmt.Errorf("Webhook response status %v", resp.Status)
}

var addToWebhookQueue = func(msg *Webhook) {
	webhookQueue <- msg
}
<commit_msg>webhooks: get proxy settings from ini file<commit_after>package notifications

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"golang.org/x/net/context/ctxhttp"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/util"
)

type Webhook struct {
	Url        string
	User       string
	Password   string
	Body       string
	HttpMethod string
	HttpHeader map[string]string
}

var netTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 5 * time.Second,
}
var netClient = &http.Client{
	Timeout:   time.Second * 30,
	Transport: netTransport,
}

var (
	webhookQueue chan *Webhook
	webhookLog   log.Logger
)

func initWebhookQueue() {
	webhookLog = log.New("notifications.webhook")
	webhookQueue = make(chan *Webhook, 10)
	go processWebhookQueue()
}

func processWebhookQueue() {
	for {
		select {
		case webhook := <-webhookQueue:
			err := sendWebRequestSync(context.Background(), webhook)

			if err != nil {
				webhookLog.Error("Failed to send webrequest ", "error", err)
			}
		}
	}
}

func sendWebRequestSync(ctx context.Context, webhook *Webhook) error {
	webhookLog.Debug("Sending webhook", "url", webhook.Url, "http method", webhook.HttpMethod)

	if webhook.HttpMethod == "" {
		webhook.HttpMethod = http.MethodPost
	}

	request, err := http.NewRequest(webhook.HttpMethod, webhook.Url, bytes.NewReader([]byte(webhook.Body)))
	if err != nil {
		return err
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "Grafana")
	if webhook.User != "" && webhook.Password != "" {
		request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))
	}

	for k, v := range webhook.HttpHeader {
		request.Header.Set(k, v)
	}

	resp, err := ctxhttp.Do(ctx, netClient, request)
	if err != nil {
		return err
	}

	if resp.StatusCode/100 == 2 {
		return nil
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	webhookLog.Debug("Webhook failed", "statuscode", resp.Status, "body", string(body))
	return fmt.Errorf("Webhook response status %v", resp.Status)
}

var addToWebhookQueue = func(msg *Webhook) {
	webhookQueue <- msg
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"image/color"
	"io"
	"sort"
	"strconv"
	"time"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/gopherjs/files"
	"github.com/MJKWoolnough/gopherjs/mutation"
	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/progress"
	"github.com/MJKWoolnough/gopherjs/xdom"
	"github.com/MJKWoolnough/gopherjs/xform"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
	"honnef.co/go/js/dom"
)

func ReadError(r *byteio.StickyReader) error {
	s := ReadString(r)
	if r.Err != nil {
		return r.Err
	}
	return errors.New(s)
}

func WriteString(w *byteio.StickyWriter, s string) {
	w.WriteUint16(uint16(len(s)))
	w.Write([]byte(s))
}

func ReadString(r *byteio.StickyReader) string {
	length := r.ReadUint16()
	str := make([]byte, int(length))
	io.ReadFull(r, str)
	return string(str)
}

func transferFile(typeName, method string, typeID uint8, o *overlay.Overlay) dom.Node {
	name := xform.InputText("name", "")
	url := xform.InputRadio("url", "switch", true)
	upload := xform.InputRadio("upload", "switch", false)
	fileI := xform.InputUpload("")
	urlI := xform.InputURL("", "")
	s := xform.InputSubmit(method)

	name.Required = true

	typeFunc := func(dom.Event) {
		if url.Checked {
			urlI.Style().RemoveProperty("display")
			fileI.Style().SetProperty("display", "none", "")
			urlI.Required = true
			fileI.Required = false
			fileI.SetID("")
			urlI.SetID("file")
		} else {
			fileI.Style().RemoveProperty("display")
			urlI.Style().SetProperty("display", "none", "")
			fileI.Required = true
			urlI.Required = false
			urlI.SetID("")
			fileI.SetID("file")
		}
	}

	typeFunc(nil)

	url.AddEventListener("change", false, typeFunc)
	upload.AddEventListener("change", false, typeFunc)

	f := xjs.AppendChildren(xdom.Form(), xjs.AppendChildren(xdom.Fieldset(),
		xjs.SetInnerText(xdom.Legend(), method+" "+typeName),
		xform.Label(typeName+" Name", "name"),
		name,
		xdom.Br(),

		xform.Label("URL", "url"),
		url,
		xdom.Br(),

		xform.Label("Upload", "upload"),
		upload,
		xdom.Br(),

		xform.Label("File", "file"),

		fileI,
		urlI,

		xdom.Br(),
		s,
	))

	s.AddEventListener("click", false, func(e dom.Event) {
		if name.Value == "" {
			return
		}
		if url.Checked {
			if urlI.Value == "" {
				return
			}
		} else if len(fileI.Files()) != 1 {
			return

		}
		s.Disabled = true
		name.Disabled = true
		url.Disabled = true
		upload.Disabled = true
		fileI.Disabled = true
		urlI.Disabled = true
		e.PreventDefault()
		go func() {
			d := xdom.Div()
			uo := overlay.New(d)
			uo.OnClose(func() {
				o.Close()
			})
			xjs.Body().AppendChild(uo)
			status := xdom.Div()
			d.AppendChild(xjs.SetInnerText(status, "Transferring..."))
			conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/transfer")
			if err != nil {
				xjs.SetInnerText(status, err.Error())
				return
			}
			defer conn.Close()
			w := byteio.StickyWriter{Writer: byteio.LittleEndianWriter{Writer: conn}}
			r := byteio.StickyReader{Reader: byteio.LittleEndianReader{Reader: conn}}

			pb := progress.New(color.RGBA{255, 0, 0, 0}, color.RGBA{0, 0, 255, 0}, 400, 50)
			d.AppendChild(pb)

			if url.Checked {
				w.WriteUint8(typeID << 1)
				WriteString(&w, urlI.Value)
				length := int(r.ReadInt32())
				total := 0
				for total < length {
					switch v := r.ReadUint8(); v {
					case 1:
						i := int(r.ReadInt32())
						total += i
						pb.Percent(100 * total / length)
					default:
						xjs.SetInnerText(status, ReadError(&r).Error())
						return
					}
				}
			} else {
				f := files.NewFileReader(files.NewFile(fileI.Files()[0]))
				l := f.Len()
				if l == 0 {
					xjs.SetInnerText(status, "Zero-length file")
					return
				}
				w.WriteUint8(typeID<<1 | 1)
				w.WriteInt32(int32(l))
				io.Copy(&w, pb.Reader(f, l))
			}

			d.RemoveChild(pb)
			xjs.SetInnerText(status, "Checking File")

			WriteString(&w, name.Value)

			canvas := xdom.Canvas()
			ctx := canvas.GetContext2d()

			for {
				switch v := r.ReadUint8(); v {
				case 0:
					if r.Err != nil {
						xjs.SetInnerText(status, r.Err.Error())
					} else {
						xjs.SetInnerText(status, ReadError(&r).Error())
					}
					return
				case 1:
					jars := make([]xform.Option, r.ReadInt16())
					for i := range jars {
						jars[i] = xform.Option{
							Value: strconv.Itoa(i),
							Label: ReadString(&r),
						}
					}
					j := xform.SelectBox("jars", jars...)
					sel := xjs.SetInnerText(xdom.Button(), "Select")
					jo := overlay.New(xjs.AppendChildren(xdom.Div(), xjs.AppendChildren(xdom.Fieldset(),
						xjs.SetInnerText(xdom.Legend(), "Please select the server jar"),
						xform.Label("Jar File", "jars"),
						j,
						xdom.Br(),
						sel,
					)))
					c := make(chan int16, 0)
					done := false
					jo.OnClose(func() {
						if !done {
							done = true
							c <- -1
						}
					})
					sel.AddEventListener("click", false, func(dom.Event) {
						if !done {
							done = true
							v, err := strconv.Atoi(j.Value)
							if err != nil {
								v = -1
							}
							c <- int16(v)
							jo.Close()
						}
					})
					xjs.Body().AppendChild(jo)
					w.WriteInt16(<-c)
					close(c)
				case 2:
					w := r.ReadInt32()
					h := r.ReadInt32()
					canvas.Width = int(w)
					canvas.Height = int(h)
					d.AppendChild(canvas)
				case 3:
					xjs.SetInnerText(status, ReadString(&r))
				case 4:
					x := r.ReadInt32()
					y := r.ReadInt32()
					red := r.ReadUint8()
					green := r.ReadUint8()
					blue := r.ReadUint8()
					alpha := r.ReadUint8()
					ctx.FillStyle = "rgba(" + strconv.Itoa(int(red)) + ", " + strconv.Itoa(int(green)) + ", " + strconv.Itoa(int(blue)) + ", " + strconv.FormatFloat(float64(alpha)/255, 'f', -1, 32) + ")"
					ctx.FillRect(int(x), int(y), 1, 1)
				case 255:
					uo.Close()
					return
				}
			}

		}()
	})
	return f
}

func editProperties(c dom.Element, name string, id int, rpcGet func(int) (map[string]string, error), rpcSet func(id int, properties map[string]string) error) {
	sp, err := rpcGet(id)
	if err != nil {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), "Failed to get properties: "+err.Error()))
		return
	}
	props := make(PropertyList, 0, len(sp))
	for k, v := range sp {
		props = append(props, [2]string{k, v})
	}
	sort.Sort(props)
	propE := make([][2]*dom.HTMLSpanElement, len(props))
	df := xjs.DocumentFragment()

	toggleFunc := func(k, v *dom.HTMLSpanElement, toggle *dom.HTMLInputElement) func(dom.Event) {
		return func(dom.Event) {
			if toggle.Value == "-" {
				k.SetContentEditable("false")
				v.SetContentEditable("false")
				k.Style().SetProperty("background-color", "#888", "")
				v.Style().SetProperty("background-color", "#888", "")
				toggle.Value = "+"
			} else {
				k.SetContentEditable("true")
				v.SetContentEditable("true")
				k.Style().RemoveProperty("background-color")
				v.Style().RemoveProperty("background-color")
				toggle.Value = "-"
			}
		}
	}

	for i, prop := range props {
		k := xform.InputSizeable("", prop[0])
		v := xform.InputSizeable("", prop[1])
		toggle := xform.InputButton("", "-")
		toggle.AddEventListener("click", false, toggleFunc(k, v, toggle))
		propE[i][0] = k
		propE[i][1] = v
		xjs.AppendChildren(df,
			toggle,
			k,
			xjs.SetInnerText(xdom.Span(), "="),
			v,
			xdom.Br(),
		)
	}

	add := xform.InputButton("", "Add")
	submit := xform.InputButton("", "Save")
	fs := xjs.AppendChildren(xdom.Fieldset(), xjs.AppendChildren(
		df,
		xjs.SetInnerText(xdom.Legend(), name+" Properties"),
		add,
		submit,
	))

	add.AddEventListener("click", false, func(dom.Event) {
		k := xform.InputSizeable("", "")
		v := xform.InputSizeable("", "")
		toggle := xform.InputButton("", "-")
		toggle.AddEventListener("click", false, toggleFunc(k, v, toggle))
		propE = append(propE, [2]*dom.HTMLSpanElement{k, v})
		fs.InsertBefore(toggle, add)
		fs.InsertBefore(k, add)
		fs.InsertBefore(xjs.SetInnerText(xdom.Span(), "="), add)
		fs.InsertBefore(v, add)
		fs.InsertBefore(xdom.Br(), add)
	})

	submit.AddEventListener("click", false, func(dom.Event) {
		submit.Disabled = true
		props := make(map[string]string, len(propE))
		for _, spans := range propE {
			if spans[0].IsContentEditable() {
				props[spans[0].TextContent()] = spans[1].TextContent()
			}
		}
		go func() {
			err := rpcSet(id, props)
			if err != nil {
				xjs.Alert("Error setting "+name+" properties: %s", err)
				return
			}
			span := xdom.Span()
			span.Style().Set("color", "#f00")
			fs.AppendChild(xjs.SetInnerText(span, "Saved!"))
			time.Sleep(5 * time.Second)
			fs.RemoveChild(span)
			submit.Disabled = false
		}()
	})

	xjs.AppendChildren(c, xjs.AppendChildren(xdom.Form(), fs))
}

func misc(mType string, id int, o *overlay.Overlay, deleteFunc func(int) error) func(dom.Element) {
	return func(c dom.Element) {
		download := xdom.A()
		download.Href = "http://" + js.Global.Get("location").Get("host").String() + "/download/" + mType + "/" + strconv.Itoa(id) + ".zip"
		del := xdom.Button()
		del.AddEventListener("click", false, func(dom.Event) {
			if dom.GetWindow().Confirm("Are you sure?") {
				err := deleteFunc(id)
				if err != nil {
					xjs.Alert("Error while deleting %s: %s", mType, err)
				} else {
					o.Close()
				}
			}
		})
		xjs.AppendChildren(c,
			xjs.AppendChildren(xdom.Fieldset(), xjs.AppendChildren(
				xjs.SetInnerText(xdom.Legend(), "Download"),
				xjs.SetInnerText(xdom.Div(), "Click the following link to download the "+mType+" as a zip file."),
				xjs.SetInnerText(download, download.Href),
			)),
			xjs.AppendChildren(xdom.Fieldset(), xjs.AppendChildren(
				xjs.SetInnerText(xdom.Legend(), "Delete"),
				xjs.SetInnerText(xdom.Div(), "The following button will permanently delete the "+mType+" (this cannot be undone)."),
				xjs.SetInnerText(del, "Delete "+mType),
			)),
		)
	}
}

func registerUpdateStopper(c dom.Element, updateStop chan struct{}) {
	if c.ParentNode() != nil {
		mutation.New(func(rs []*mutation.Record, o *mutation.Observer) {
			if len(rs) > 0 {
				for _, r := range rs {
					for _, n := range r.RemovedNodes() {
						if c.IsEqualNode(n) {
							o.Disconnect()
							close(updateStop)
							return
						}
					}
				}
			}
		}).Observe(c.ParentNode(), mutation.ObserverInit{ChildList: true})
	}
}

func updateSleep(forceUpdate, updateStop <-chan struct{}) bool {
	t := time.NewTicker(time.Second * 30)
	defer t.Stop()
	select {
	case <-updateStop:
		return false
	case <-forceUpdate:
	case <-t.C:
	}
	return true
}
<commit_msg>Corrected dom error<commit_after>package main

import (
	"errors"
	"image/color"
	"io"
	"sort"
	"strconv"
	"time"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/gopherjs/files"
	"github.com/MJKWoolnough/gopherjs/mutation"
	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/progress"
	"github.com/MJKWoolnough/gopherjs/xdom"
	"github.com/MJKWoolnough/gopherjs/xform"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/websocket"
	"honnef.co/go/js/dom"
)

func ReadError(r *byteio.StickyReader) error {
	s := ReadString(r)
	if r.Err != nil {
		return r.Err
	}
	return errors.New(s)
}

func WriteString(w *byteio.StickyWriter, s string) {
	w.WriteUint16(uint16(len(s)))
	w.Write([]byte(s))
}

func ReadString(r *byteio.StickyReader) string {
	length := r.ReadUint16()
	str := make([]byte, int(length))
	io.ReadFull(r, str)
	return string(str)
}

func transferFile(typeName, method string, typeID uint8, o *overlay.Overlay) dom.Node {
	name := xform.InputText("name", "")
	url := xform.InputRadio("url", "switch", true)
	upload := xform.InputRadio("upload", "switch", false)
	fileI := xform.InputUpload("")
	urlI := xform.InputURL("", "")
	s := xform.InputSubmit(method)

	name.Required = true

	typeFunc := func(dom.Event) {
		if url.Checked {
			urlI.Style().RemoveProperty("display")
			fileI.Style().SetProperty("display", "none", "")
			urlI.Required = true
			fileI.Required = false
			fileI.SetID("")
			urlI.SetID("file")
		} else {
			fileI.Style().RemoveProperty("display")
			urlI.Style().SetProperty("display", "none", "")
			fileI.Required = true
			urlI.Required = false
			urlI.SetID("")
			fileI.SetID("file")
		}
	}

	typeFunc(nil)

	url.AddEventListener("change", false, typeFunc)
	upload.AddEventListener("change", false, typeFunc)

	f := xjs.AppendChildren(xdom.Form(), xjs.AppendChildren(xdom.Fieldset(),
		xjs.SetInnerText(xdom.Legend(), method+" "+typeName),
		xform.Label(typeName+" Name", "name"),
		name,
		xdom.Br(),

		xform.Label("URL", "url"),
		url,
		xdom.Br(),

		xform.Label("Upload", "upload"),
		upload,
		xdom.Br(),

		xform.Label("File", "file"),

		fileI,
		urlI,

		xdom.Br(),
		s,
	))

	s.AddEventListener("click", false, func(e dom.Event) {
		if name.Value == "" {
			return
		}
		if url.Checked {
			if urlI.Value == "" {
				return
			}
		} else if len(fileI.Files()) != 1 {
			return

		}
		s.Disabled = true
		name.Disabled = true
		url.Disabled = true
		upload.Disabled = true
		fileI.Disabled = true
		urlI.Disabled = true
		e.PreventDefault()
		go func() {
			d := xdom.Div()
			uo := overlay.New(d)
			uo.OnClose(func() {
				o.Close()
			})
			xjs.Body().AppendChild(uo)
			status := xdom.Div()
			d.AppendChild(xjs.SetInnerText(status, "Transferring..."))
			conn, err := websocket.Dial("ws://" + js.Global.Get("location").Get("host").String() + "/transfer")
			if err != nil {
				xjs.SetInnerText(status, err.Error())
				return
			}
			defer conn.Close()
			w := byteio.StickyWriter{Writer: byteio.LittleEndianWriter{Writer: conn}}
			r := byteio.StickyReader{Reader: byteio.LittleEndianReader{Reader: conn}}

			pb := progress.New(color.RGBA{255, 0, 0, 0}, color.RGBA{0, 0, 255, 0}, 400, 50)
			d.AppendChild(pb)

			if url.Checked {
				w.WriteUint8(typeID << 1)
				WriteString(&w, urlI.Value)
				length := int(r.ReadInt32())
				total := 0
				for total < length {
					switch v := r.ReadUint8(); v {
					case 1:
						i := int(r.ReadInt32())
						total += i
						pb.Percent(100 * total / length)
					default:
						xjs.SetInnerText(status, ReadError(&r).Error())
						return
					}
				}
			} else {
				f := files.NewFileReader(files.NewFile(fileI.Files()[0]))
				l := f.Len()
				if l == 0 {
					xjs.SetInnerText(status, "Zero-length file")
					return
				}
				w.WriteUint8(typeID<<1 | 1)
				w.WriteInt32(int32(l))
				io.Copy(&w, pb.Reader(f, l))
			}

			d.RemoveChild(pb)
			xjs.SetInnerText(status, "Checking File")

			WriteString(&w, name.Value)

			canvas := xdom.Canvas()
			ctx := canvas.GetContext2d()

			for {
				switch v := r.ReadUint8(); v {
				case 0:
					if r.Err != nil {
						xjs.SetInnerText(status, r.Err.Error())
					} else {
						xjs.SetInnerText(status, ReadError(&r).Error())
					}
					return
				case 1:
					jars := make([]xform.Option, r.ReadInt16())
					for i := range jars {
						jars[i] = xform.Option{
							Value: strconv.Itoa(i),
							Label: ReadString(&r),
						}
					}
					j := xform.SelectBox("jars", jars...)
					sel := xjs.SetInnerText(xdom.Button(), "Select")
					jo := overlay.New(xjs.AppendChildren(xdom.Div(), xjs.AppendChildren(xdom.Fieldset(),
						xjs.SetInnerText(xdom.Legend(), "Please select the server jar"),
						xform.Label("Jar File", "jars"),
						j,
						xdom.Br(),
						sel,
					)))
					c := make(chan int16, 0)
					done := false
					jo.OnClose(func() {
						if !done {
							done = true
							c <- -1
						}
					})
					sel.AddEventListener("click", false, func(dom.Event) {
						if !done {
							done = true
							v, err := strconv.Atoi(j.Value)
							if err != nil {
								v = -1
							}
							c <- int16(v)
							jo.Close()
						}
					})
					xjs.Body().AppendChild(jo)
					w.WriteInt16(<-c)
					close(c)
				case 2:
					w := r.ReadInt32()
					h := r.ReadInt32()
					canvas.Width = int(w)
					canvas.Height = int(h)
					d.AppendChild(canvas)
				case 3:
					xjs.SetInnerText(status, ReadString(&r))
				case 4:
					x := r.ReadInt32()
					y := r.ReadInt32()
					red := r.ReadUint8()
					green := r.ReadUint8()
					blue := r.ReadUint8()
					alpha := r.ReadUint8()
					ctx.FillStyle = "rgba(" + strconv.Itoa(int(red)) + ", " + strconv.Itoa(int(green)) + ", " + strconv.Itoa(int(blue)) + ", " + strconv.FormatFloat(float64(alpha)/255, 'f', -1, 32) + ")"
					ctx.FillRect(int(x), int(y), 1, 1)
				case 255:
					uo.Close()
					return
				}
			}

		}()
	})
	return f
}

func editProperties(c dom.Element, name string, id int, rpcGet func(int) (map[string]string, error), rpcSet func(id int, properties map[string]string) error) {
	sp, err := rpcGet(id)
	if err != nil {
		c.AppendChild(xjs.SetInnerText(xdom.Div(), "Failed to get properties: "+err.Error()))
		return
	}
	props := make(PropertyList, 0, len(sp))
	for k, v := range sp {
		props = append(props, [2]string{k, v})
	}
	sort.Sort(props)
	propE := make([][2]*dom.HTMLSpanElement, len(props))
	df := xjs.DocumentFragment()

	toggleFunc := func(k, v *dom.HTMLSpanElement, toggle *dom.HTMLInputElement) func(dom.Event) {
		return func(dom.Event) {
			if toggle.Value == "-" {
				k.SetContentEditable("false")
				v.SetContentEditable("false")
				k.Style().SetProperty("background-color", "#888", "")
				v.Style().SetProperty("background-color", "#888", "")
				toggle.Value = "+"
			} else {
				k.SetContentEditable("true")
				v.SetContentEditable("true")
				k.Style().RemoveProperty("background-color")
				v.Style().RemoveProperty("background-color")
				toggle.Value = "-"
			}
		}
	}

	for i, prop := range props {
		k := xform.InputSizeable("", prop[0])
		v := xform.InputSizeable("", prop[1])
		toggle := xform.InputButton("", "-")
		toggle.AddEventListener("click", false, toggleFunc(k, v, toggle))
		propE[i][0] = k
		propE[i][1] = v
		xjs.AppendChildren(df,
			toggle,
			k,
			xjs.SetInnerText(xdom.Span(), "="),
			v,
			xdom.Br(),
		)
	}

	add := xform.InputButton("", "Add")
	submit := xform.InputButton("", "Save")
	fs := xjs.AppendChildren(xdom.Fieldset(), xjs.AppendChildren(
		df,
		xjs.SetInnerText(xdom.Legend(), name+" Properties"),
		add,
		submit,
	))

	add.AddEventListener("click", false, func(dom.Event) {
		k := xform.InputSizeable("", "")
		v := xform.InputSizeable("", "")
		toggle := xform.InputButton("", "-")
		toggle.AddEventListener("click", false, toggleFunc(k, v, toggle))
		propE = append(propE, [2]*dom.HTMLSpanElement{k, v})
		fs.InsertBefore(toggle, add)
		fs.InsertBefore(k, add)
		fs.InsertBefore(xjs.SetInnerText(xdom.Span(), "="), add)
		fs.InsertBefore(v, add)
		fs.InsertBefore(xdom.Br(), add)
	})

	submit.AddEventListener("click", false, func(dom.Event) {
		submit.Disabled = true
		props := make(map[string]string, len(propE))
		for _, spans := range propE {
			if spans[0].IsContentEditable() {
				props[spans[0].TextContent()] = spans[1].TextContent()
			}
		}
		go func() {
			err := rpcSet(id, props)
			if err != nil {
				xjs.Alert("Error setting "+name+" properties: %s", err)
				return
			}
			span := xdom.Span()
			span.Style().Set("color", "#f00")
			fs.AppendChild(xjs.SetInnerText(span, "Saved!"))
			time.Sleep(5 * time.Second)
			fs.RemoveChild(span)
			submit.Disabled = false
		}()
	})

	xjs.AppendChildren(c, xjs.AppendChildren(xdom.Form(), fs))
}

func misc(mType string, id int, o *overlay.Overlay, deleteFunc func(int) error) func(dom.Element) {
	return func(c dom.Element) {
		download := xdom.A()
		download.Href = "http://" + js.Global.Get("location").Get("host").String() + "/download/" + mType + "/" + strconv.Itoa(id) + ".zip"
		del := xdom.Button()
		del.AddEventListener("click", false, func(dom.Event) {
			if dom.GetWindow().Confirm("Are you sure?") {
				err := deleteFunc(id)
				if err != nil {
					xjs.Alert("Error while deleting %s: %s", mType, err)
				} else {
					o.Close()
				}
			}
		})
		xjs.AppendChildren(c,
			xjs.AppendChildren(xdom.Fieldset(),
				xjs.SetInnerText(xdom.Legend(), "Download"),
				xjs.SetInnerText(xdom.Div(), "Click the following link to download the "+mType+" as a zip file."),
				xjs.SetInnerText(download, download.Href),
			),
			xjs.AppendChildren(xdom.Fieldset(),
				xjs.SetInnerText(xdom.Legend(), "Delete"),
				xjs.SetInnerText(xdom.Div(), "The following button will permanently delete the "+mType+" (this cannot be undone)."),
				xjs.SetInnerText(del, "Delete "+mType),
			),
		)
	}
}

func registerUpdateStopper(c dom.Element, updateStop chan struct{}) {
	if c.ParentNode() != nil {
		mutation.New(func(rs []*mutation.Record, o *mutation.Observer) {
			if len(rs) > 0 {
				for _, r := range rs {
					for _, n := range r.RemovedNodes() {
						if c.IsEqualNode(n) {
							o.Disconnect()
							close(updateStop)
							return
						}
					}
				}
			}
		}).Observe(c.ParentNode(), mutation.ObserverInit{ChildList: true})
	}
}

func updateSleep(forceUpdate, updateStop <-chan struct{}) bool {
	t := time.NewTicker(time.Second * 30)
	defer t.Stop()
	select {
	case <-updateStop:
		return false
	case <-forceUpdate:
	case <-t.C:
	}
	return true
}
<|endoftext|>


<commit_before>package nico

import "strings"

type Mail struct {
	Is184 bool
}

func (m Mail) String() string {
	var strs []string
	if m.Is184 {
		strs = append(strs, "184")
	}
	return strings.Join(strs, " ")
}
<commit_msg>Add godoc for the Mail struct<commit_after>package nico

import "strings"

// Mail is a structure that specifies comment options.
type Mail struct {
	Is184 bool
}

func (m Mail) String() string {
	var strs []string
	if m.Is184 {
		strs = append(strs, "184")
	}
	return strings.Join(strs, " ")
}
<|endoftext|>


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

package http

import (
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestRetry(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type RetryingConnTest struct {
}

func init() { RegisterTestSuite(&RetryingConnTest{}) }

func (t *RetryingConnTest) SetUp(i *TestInfo) {
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *RetryingConnTest) CallsWrapped() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsWrongErrorType() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsWrongOpErrorType() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsUnknownErrno() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) RetriesForBrokenPipe() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedFailsOnThirdCall() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedSucceedsOnThirdCall() {
	ExpectEq("TODO", "")
}
<commit_msg>Added members.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package http

import (
	"github.com/jacobsa/aws/s3/http/mock"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestRetry(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type RetryingConnTest struct {
	wrapped mock_http.MockConn
	conn Conn

	req *Request
	resp *Response
	err error
}

func init() { RegisterTestSuite(&RetryingConnTest{}) }

func (t *RetryingConnTest) SetUp(i *TestInfo) {
	var err error

	t.wrapped = mock_http.NewMockConn(i.MockController, "wrapped")
	t.conn, err = newRetryingConn(t.wrapped)
	AssertEq(nil, err)
}

func (t *RetryingConnTest) call() {
	t.resp, t.err = t.conn.SendRequest(t.req)
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *RetryingConnTest) CallsWrapped() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsWrongErrorType() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsWrongOpErrorType() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedReturnsUnknownErrno() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) RetriesForBrokenPipe() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedFailsOnThirdCall() {
	ExpectEq("TODO", "")
}

func (t *RetryingConnTest) WrappedSucceedsOnThirdCall() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package s3proxy

import (
	"errors"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"net/http"
	"os"
	"io"
	"log"
	"strings"
	"time"
	"bytes"
)

type S3BucketFileSystem struct {
	Bucket string
	sss *s3.S3
}

func NewS3BucketFileSystem(bucketName string, config *aws.Config) *S3BucketFileSystem {
	return &S3BucketFileSystem{
		Bucket: bucketName,
		sss: s3.New(config),
	}
}

func (self *S3BucketFileSystem) Open(name string) (http.File, error) {
	log.Printf("trying to open %s", name)
	params := s3.GetObjectInput{
		Bucket: aws.String(self.Bucket),
		Key: aws.String(name),
	}
	if !strings.HasSuffix(name, "/")  {
		resp, err := self.sss.GetObject(&params)
		if err == nil {
			bko := s3BucketObject{
				Key: name,
				PossibleDir: false,
				response: resp,
				fileReader: nil,
			}
			err = bko.createFileReader()
			return &bko, err
		}
	}

	// If we're here, the path MIGHT represent a "directory" in S3
	// To determine if it exists, we must ask S3 for a list of all objects in
	// the bucket that have the prefix of the path.
	dirKey := strings.TrimLeft(name, "/")
	var maxKeys int64 = 100
	listParams := s3.ListObjectsInput{
		Bucket: params.Bucket,
		Prefix: aws.String(dirKey),
		MaxKeys: &maxKeys,
	}
	resp, err := self.sss.ListObjects(&listParams)
	if err != nil {
		return nil, err
	}
	// If it contains nothing, then the directory doesn't exist
	if len(resp.Contents) <= 0 {
		return nil, os.ErrNotExist
	}

	return &s3BucketDirectory {
		Key: *params.Key,
		response: resp,
	}, nil
}

type s3BucketDirEntry struct {
	obj *s3.Object
}

func (self s3BucketDirEntry) Name() string {
	return *self.obj.Key
}

func (self s3BucketDirEntry) Size() int64 {
	return *self.obj.Size
}

func (self s3BucketDirEntry) Mode() os.FileMode {
	return s3BucketObjectMode
}

func (self s3BucketDirEntry) ModTime() time.Time {
	return *self.obj.LastModified
}

func (self s3BucketDirEntry) IsDir() bool {
	return false
}

func (self s3BucketDirEntry) Sys() interface{} {
	return nil
}

type s3BucketDirectory struct {
	Key string
	response *s3.ListObjectsOutput
}

func (self *s3BucketDirectory) Read(p []byte) (int, error) {
	return 0, nil
}

func (self *s3BucketDirectory) Close() error {
	return nil
}

func (self *s3BucketDirectory) Seek(offset int64, whence int) (int64, error) {
	return 0, errors.New("Unsupported operation: Seek on directory")
}

func (self *s3BucketDirectory) Readdir(count int) ([]os.FileInfo, error) {
	nEntries := len(self.response.Contents)
	entries := make([]os.FileInfo, nEntries, nEntries)
	for i := range self.response.Contents {
		entries[i] = &s3BucketDirEntry{
			obj: self.response.Contents[i],
		}
	}
	return entries, nil
}

func (self *s3BucketDirectory) Stat() (os.FileInfo, error) {
	return self, nil
}

func (self *s3BucketDirectory) Name() string {
	return self.Key
}

func (self *s3BucketDirectory) Size() int64 {
	return 0
}

func (self *s3BucketDirectory) Mode() os.FileMode {
	return os.ModeDir
}

func (self *s3BucketDirectory) ModTime() time.Time {
	return time.Now()
}

func (self *s3BucketDirectory) IsDir() bool {
	return true
}

func (self *s3BucketDirectory) Sys() interface{} {
	return nil
}

const s3BucketObjectMode os.FileMode = 0444

type s3BucketObject struct {
	Key string
	PossibleDir bool
	response *s3.GetObjectOutput
	fileReader *bytes.Reader
}

func (self *s3BucketObject) createFileReader() error {
	buf := bytes.NewBuffer([]byte{})
	l, err := io.Copy(buf, self.response.Body)
	if err != nil {
		return err
	}
	if l != self.Size() {
		return errors.New("Content length mismatch")
	}
	self.fileReader = bytes.NewReader(buf.Bytes())
	return nil
}

func (self *s3BucketObject) Read(p []byte) (int, error) {
	if self.response != nil {
		if self.fileReader == nil {
			return self.response.Body.Read(p)
		} else {
			return self.fileReader.Read(p)
		}
	} else {
		return -1, errors.New("Cannot read a non-existant file or directory!")
	}
}

func (self *s3BucketObject) Seek(offset int64, whence int) (int64, error) {
	if self.fileReader == nil {
		buf := bytes.NewBuffer([]byte{})
		l, err := io.Copy(buf, self.response.Body)
		if err != nil {
			return 0, err
		}
		if l != self.Size() {
			return l, errors.New("Content length mismatch")
		}
		self.fileReader = bytes.NewReader(buf.Bytes())
	}
	return self.fileReader.Seek(offset, whence)
}

func (self *s3BucketObject) Close() error {
	if self.response != nil {
		return self.response.Body.Close()
	}
	return nil
}

func (self *s3BucketObject) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("Unsupported operation: Readdir")
}

func (self *s3BucketObject) Stat() (os.FileInfo, error) {
	return self, nil
}

func (self *s3BucketObject) Name() string {
	return self.Key
}

func (self *s3BucketObject) Size() int64 {
	if self.response != nil {
		return *self.response.ContentLength
	} else {
		return 0
	}
}

func (self *s3BucketObject) Mode() os.FileMode {
	if self.IsDir() {
		return os.ModeDir
	} else {
		return s3BucketObjectMode
	}
}

func (self *s3BucketObject) ModTime() time.Time {
	if self.response != nil {
		return *self.response.LastModified
	} else {
		return time.Now()
	}
}

func (self *s3BucketObject) IsDir() bool {
	return self.PossibleDir
}

func (self *s3BucketObject) Sys() interface{} {
	return nil
}
<commit_msg>readdir: Fix readdir recursion<commit_after>package s3proxy

import (
	"errors"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"net/http"
	"os"
	"io"
	"log"
	"strings"
	"time"
	"bytes"
)

type S3BucketFileSystem struct {
	Bucket string
	sss *s3.S3
}

func NewS3BucketFileSystem(bucketName string, config *aws.Config) *S3BucketFileSystem {
	return &S3BucketFileSystem{
		Bucket: bucketName,
		sss: s3.New(config),
	}
}

func (self *S3BucketFileSystem) Open(name string) (http.File, error) {
	log.Printf("trying to open %s", name)
	params := s3.GetObjectInput{
		Bucket: aws.String(self.Bucket),
		Key: aws.String(name),
	}
	if !strings.HasSuffix(name, "/")  {
		resp, err := self.sss.GetObject(&params)
		if err == nil {
			bko := s3BucketObject{
				Key: name,
				PossibleDir: false,
				response: resp,
				fileReader: nil,
			}
			err = bko.createFileReader()
			return &bko, err
		}
	}

	// If we're here, the path MIGHT represent a "directory" in S3
	// To determine if it exists, we must ask S3 for a list of all objects in
	// the bucket that have the prefix of the path.
	dirKey := strings.TrimLeft(name, "/")
	var maxKeys int64 = 1000
	listParams := s3.ListObjectsInput{
		Bucket: params.Bucket,
		Prefix: aws.String(dirKey),
		MaxKeys: &maxKeys,
	}
	resp, err := self.sss.ListObjects(&listParams)
	if err != nil {
		return nil, err
	}
	// If it contains nothing, then the directory doesn't exist
	if len(resp.Contents) <= 0 {
		return nil, os.ErrNotExist
	}

	return &s3BucketDirectory {
		Key: *params.Key,
		response: resp,
	}, nil
}

type s3BucketDirEntry struct {
	DirKey string
	obj *s3.Object
}

func (self s3BucketDirEntry) Name() string {
	toTrim := strings.TrimLeft(self.DirKey, "/") + "/"
	trimmed := strings.TrimLeft(*self.obj.Key, toTrim)
	return trimmed
}

func (self s3BucketDirEntry) Size() int64 {
	return *self.obj.Size
}

func (self s3BucketDirEntry) Mode() os.FileMode {
	return s3BucketObjectMode
}

func (self s3BucketDirEntry) ModTime() time.Time {
	return *self.obj.LastModified
}

func (self s3BucketDirEntry) IsDir() bool {
	return false
}

func (self s3BucketDirEntry) Sys() interface{} {
	return nil
}

type s3BucketDirectory struct {
	Key string
	position int
	response *s3.ListObjectsOutput
}

func (self *s3BucketDirectory) Read(p []byte) (int, error) {
	return 0, nil
}

func (self *s3BucketDirectory) Close() error {
	return nil
}

func (self *s3BucketDirectory) Seek(offset int64, whence int) (int64, error) {
	return 0, errors.New("Unsupported operation: Seek on directory")
}

func (self *s3BucketDirectory) Readdir(count int) ([]os.FileInfo, error) {
	nEntries := len(self.response.Contents)
	log.Printf("Directory (%s) has %d contents\n", self.Key, nEntries)
	entries := make([]os.FileInfo, 0, nEntries)
	entriesChan := make(chan os.FileInfo)
	go func() {
		defer close(entriesChan)
		n := 0
		for i := self.position; i < len(self.response.Contents); i++ {
			n++
			if n > count {
				self.position = i - 1
				return
			}

			e := &s3BucketDirEntry{
				DirKey: self.Key,
				obj: self.response.Contents[i],
			}
			if len(e.Name()) != 0 {
				entriesChan <- e
			}
		}
		if n <= count {
			entriesChan <- nil
		}
	}()
	var err error = nil
	for e := range entriesChan {
		if e == nil {
			err = io.EOF
		} else {
			entries = append(entries, e)
		}
	}
	return entries, err
}

func (self *s3BucketDirectory) Stat() (os.FileInfo, error) {
	return self, nil
}

func (self *s3BucketDirectory) Name() string {
	return self.Key
}

func (self *s3BucketDirectory) Size() int64 {
	var contents int64 = int64(len(self.response.Contents))
	contents -= 1
	return contents
}

func (self *s3BucketDirectory) Mode() os.FileMode {
	return os.ModeDir
}

func (self *s3BucketDirectory) ModTime() time.Time {
	return time.Now()
}

func (self *s3BucketDirectory) IsDir() bool {
	return true
}

func (self *s3BucketDirectory) Sys() interface{} {
	return nil
}

const s3BucketObjectMode os.FileMode = 0444

type s3BucketObject struct {
	Key string
	PossibleDir bool
	response *s3.GetObjectOutput
	fileReader *bytes.Reader
}

func (self *s3BucketObject) createFileReader() error {
	buf := bytes.NewBuffer([]byte{})
	l, err := io.Copy(buf, self.response.Body)
	if err != nil {
		return err
	}
	if l != self.Size() {
		return errors.New("Content length mismatch")
	}
	self.fileReader = bytes.NewReader(buf.Bytes())
	return nil
}

func (self *s3BucketObject) Read(p []byte) (int, error) {
	if self.response != nil {
		if self.fileReader == nil {
			return self.response.Body.Read(p)
		} else {
			return self.fileReader.Read(p)
		}
	} else {
		return -1, errors.New("Cannot read a non-existant file or directory!")
	}
}

func (self *s3BucketObject) Seek(offset int64, whence int) (int64, error) {
	if self.fileReader == nil {
		buf := bytes.NewBuffer([]byte{})
		l, err := io.Copy(buf, self.response.Body)
		if err != nil {
			return 0, err
		}
		if l != self.Size() {
			return l, errors.New("Content length mismatch")
		}
		self.fileReader = bytes.NewReader(buf.Bytes())
	}
	return self.fileReader.Seek(offset, whence)
}

func (self *s3BucketObject) Close() error {
	if self.response != nil {
		return self.response.Body.Close()
	}
	return nil
}

func (self *s3BucketObject) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("Unsupported operation: Readdir")
}

func (self *s3BucketObject) Stat() (os.FileInfo, error) {
	return self, nil
}

func (self *s3BucketObject) Name() string {
	return self.Key
}

func (self *s3BucketObject) Size() int64 {
	if self.response != nil {
		return *self.response.ContentLength
	} else {
		return 0
	}
}

func (self *s3BucketObject) Mode() os.FileMode {
	if self.IsDir() {
		return os.ModeDir
	} else {
		return s3BucketObjectMode
	}
}

func (self *s3BucketObject) ModTime() time.Time {
	if self.response != nil {
		return *self.response.LastModified
	} else {
		return time.Now()
	}
}

func (self *s3BucketObject) IsDir() bool {
	return self.PossibleDir
}

func (self *s3BucketObject) Sys() interface{} {
	return nil
}
<|endoftext|>


<commit_before>package conf

import (
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	beego "github.com/astaxie/beego/logs"
	"github.com/sirupsen/logrus"
)

// GOPATH contains the gopath of the env
var GOPATH string

// LogDir is the log directory for logs.json
var LogDir string

// LogFile name
var LogFile string

func init() {
	GOPATH = os.Getenv("GOPATH")
	if GOPATH == "" {
		GOPATH = defaultGOPATH()
	}

	appPath := GOPATH + "/src/github.com/snagles/docker-registry-manager/app"
	LogDir = appPath + "/logs/"
	LogFile = LogDir + "/log.json"

	// create log dir if needed
	if _, err := os.Stat(LogDir); os.IsNotExist(err) {
		if err = os.Mkdir(LogDir, 0755); err != nil {
			logrus.Fatal(err)
		}
	}

	// Create the log file if needed
	if _, err := os.Stat(LogFile); os.IsNotExist(err) {
		if _, err = os.Create(LogFile); err != nil {
			logrus.Fatal(err)
		}
	}

	// Setup logrus
	f, _ := os.OpenFile(LogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)

	// log to file and stdout
	logrus.SetOutput(f)
	logrus.SetOutput(os.Stdout)

	logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.AddHook(ContextHook{})

	// Setup Beego logging by registering the hook
	beego.Register("docker-registry-manager", NewBeegoHook)
	beego.SetLogger("docker-registry-manager", "")
	beego.EnableFuncCallDepth(true)
}

type registryLogger struct {
	Level int `json:"level"`
}

func NewBeegoHook() beego.Logger { return &registryLogger{Level: 0} }

// Init returns nothing since theres no additional config optios needed
func (rl *registryLogger) Init(jsonconfig string) error { return nil }

// Destroy is a empty method
func (rl *registryLogger) Destroy() {}

// Flush is a empty method
func (rl *registryLogger) Flush() {}

// WriteMsg will write the msg and level into es
func (rl *registryLogger) WriteMsg(when time.Time, msg string, level int) error {
	if level < int(logrus.GetLevel()) {
		return nil
	}

	switch level {
	// beego is reverse order
	case 0:
		logrus.Panic(msg)
	case 1:
		logrus.Panic(msg)
	case 2:
		logrus.Panic(msg)
	case 3:
		logrus.Error(msg)
	case 4:
		logrus.Warn(msg)
	case 5:
		logrus.Info(msg)
	case 6:
		logrus.Info(msg)
	case 7:
		logrus.Debug(msg)
	}
	return nil
}

type ContextHook struct{}

func (hook ContextHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

func (hook ContextHook) Fire(entry *logrus.Entry) error {
	pc := make([]uintptr, 3, 3)
	cnt := runtime.Callers(6, pc)

	for i := 0; i < cnt; i++ {
		fu := runtime.FuncForPC(pc[i] - 1)
		name := fu.Name()
		if !strings.Contains(name, "github.com/sirupsen/logrus") {

			if strings.Contains(name, "registryLogger") {
				// Remove the prefix beego attaches
				r := regexp.MustCompile(`\[[A-Z]\] \[(.*):(.*)\] (.*)`)
				message := r.FindAllStringSubmatch(entry.Message, 1)

				// add the caller as a separate field
				entry.Data["file"] = message[0][1]
				line, _ := strconv.Atoi(message[0][2])
				entry.Data["line"] = line
				entry.Message = message[0][3]
				entry.Data["source"] = "beego"
				break
			} else {
				file, line := fu.FileLine(pc[i] - 1)
				entry.Data["file"] = path.Base(file)
				entry.Data["line"] = line
				entry.Data["source"] = "app"
				break
			}
		}

	}
	return nil
}

func defaultGOPATH() string {
	env := "HOME"
	if runtime.GOOS == "windows" {
		env = "USERPROFILE"
	} else if runtime.GOOS == "plan9" {
		env = "home"
	}
	if home := os.Getenv(env); home != "" {
		def := filepath.Join(home, "go")
		if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
			// Don't set the default GOPATH to GOROOT,
			// as that will trigger warnings from the go tool.
			return ""
		}
		return def
	}
	return ""
}
<commit_msg>Changed JSON stdout logs to use the logrus format<commit_after>package conf

import (
	"io"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	beego "github.com/astaxie/beego/logs"
	"github.com/sirupsen/logrus"
)

// GOPATH contains the gopath of the env
var GOPATH string

// LogDir is the log directory for logs.json
var LogDir string

// LogFile name
var LogFile string

func init() {
	GOPATH = os.Getenv("GOPATH")
	if GOPATH == "" {
		GOPATH = defaultGOPATH()
	}

	appPath := GOPATH + "/src/github.com/snagles/docker-registry-manager/app"
	LogDir = appPath + "/logs/"
	LogFile = LogDir + "/log.json"

	// create log dir if needed
	if _, err := os.Stat(LogDir); os.IsNotExist(err) {
		if err = os.Mkdir(LogDir, 0755); err != nil {
			logrus.Fatal(err)
		}
	}

	// Create the log file if needed
	if _, err := os.Stat(LogFile); os.IsNotExist(err) {
		if _, err = os.Create(LogFile); err != nil {
			logrus.Fatal(err)
		}
	}

	// Setup logrus with the json events log
	f, _ := os.OpenFile(LogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
	logrus.AddHook(NewFileHook(f, &logrus.JSONFormatter{}))

	// log to std out for docker logs
	logrus.SetOutput(os.Stdout)
	logrus.AddHook(ContextHook{})

	// Connect the beego logs to the other logs
	beego.Register("docker-registry-manager", NewBeegoHook)
	beego.SetLogger("docker-registry-manager", "")
	beego.EnableFuncCallDepth(true)
}

type registryLogger struct {
	Level int `json:"level"`
}

func NewBeegoHook() beego.Logger { return &registryLogger{Level: 0} }

// Init returns nothing since theres no additional config optios needed
func (rl *registryLogger) Init(jsonconfig string) error { return nil }

// Destroy is a empty method
func (rl *registryLogger) Destroy() {}

// Flush is a empty method
func (rl *registryLogger) Flush() {}

// WriteMsg will write the msg and level into es
func (rl *registryLogger) WriteMsg(when time.Time, msg string, level int) error {
	if level < int(logrus.GetLevel()) {
		return nil
	}

	switch level {
	// beego is reverse order
	case 0:
		logrus.Panic(msg)
	case 1:
		logrus.Panic(msg)
	case 2:
		logrus.Panic(msg)
	case 3:
		logrus.Error(msg)
	case 4:
		logrus.Warn(msg)
	case 5:
		logrus.Info(msg)
	case 6:
		logrus.Info(msg)
	case 7:
		logrus.Debug(msg)
	}
	return nil
}

type ContextHook struct{}

func (hook ContextHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

func (hook ContextHook) Fire(entry *logrus.Entry) error {
	pc := make([]uintptr, 3, 3)
	cnt := runtime.Callers(6, pc)

	for i := 0; i < cnt; i++ {
		fu := runtime.FuncForPC(pc[i] - 1)
		name := fu.Name()
		if !strings.Contains(name, "github.com/sirupsen/logrus") {

			if strings.Contains(name, "registryLogger") {
				// Remove the prefix beego attaches
				r := regexp.MustCompile(`\[[A-Z]\] \[(.*):(.*)\] (.*)`)
				message := r.FindAllStringSubmatch(entry.Message, 1)

				// add the caller as a separate field
				entry.Data["file"] = message[0][1]
				line, _ := strconv.Atoi(message[0][2])
				entry.Data["line"] = line
				entry.Message = message[0][3]
				entry.Data["source"] = "beego"
				break
			} else {
				file, line := fu.FileLine(pc[i] - 1)
				entry.Data["file"] = path.Base(file)
				entry.Data["line"] = line
				entry.Data["source"] = "app"
				break
			}
		}

	}
	return nil
}

type FileHook struct {
	writer    io.Writer
	formatter logrus.Formatter
}

func NewFileHook(w io.Writer, f logrus.Formatter) logrus.Hook {
	return FileHook{
		writer:    w,
		formatter: f,
	}
}

func (h FileHook) Fire(e *logrus.Entry) error {
	b, err := h.formatter.Format(e)
	if err != nil {
		return err
	}
	_, err = h.writer.Write(b)
	return err
}

// Levels returns all logrus levels.
func (h FileHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

func defaultGOPATH() string {
	env := "HOME"
	if runtime.GOOS == "windows" {
		env = "USERPROFILE"
	} else if runtime.GOOS == "plan9" {
		env = "home"
	}
	if home := os.Getenv(env); home != "" {
		def := filepath.Join(home, "go")
		if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
			// Don't set the default GOPATH to GOROOT,
			// as that will trigger warnings from the go tool.
			return ""
		}
		return def
	}
	return ""
}
<|endoftext|>


<commit_before>// Copyright 2013 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	. "github.com/lxn/go-winapi"
)

type Key uint16

const (
	KeyLButton           Key = VK_LBUTTON
	KeyRButton               = VK_RBUTTON
	KeyCancel                = VK_CANCEL
	KeyMButton               = VK_MBUTTON
	KeyXButton1              = VK_XBUTTON1
	KeyXButton2              = VK_XBUTTON2
	KeyBack                  = VK_BACK
	KeyTab                   = VK_TAB
	KeyClear                 = VK_CLEAR
	KeyReturn                = VK_RETURN
	KeyShift                 = VK_SHIFT
	KeyControl               = VK_CONTROL
	KeyMenu                  = VK_MENU
	KeyPause                 = VK_PAUSE
	KeyCapital               = VK_CAPITAL
	KeyKana                  = VK_KANA
	KeyHangul                = VK_HANGUL
	KeyJunja                 = VK_JUNJA
	KeyFinal                 = VK_FINAL
	KeyHanja                 = VK_HANJA
	KeyKanji                 = VK_KANJI
	KeyEscape                = VK_ESCAPE
	KeyConvert               = VK_CONVERT
	KeyNonconvert            = VK_NONCONVERT
	KeyAccept                = VK_ACCEPT
	KeyModeChange            = VK_MODECHANGE
	KeySpace                 = VK_SPACE
	KeyPrior                 = VK_PRIOR
	KeyNext                  = VK_NEXT
	KeyEnd                   = VK_END
	KeyHome                  = VK_HOME
	KeyLeft                  = VK_LEFT
	KeyUp                    = VK_UP
	KeyRight                 = VK_RIGHT
	KeyDown                  = VK_DOWN
	KeySelect                = VK_SELECT
	KeyPrint                 = VK_PRINT
	KeyExecute               = VK_EXECUTE
	KeySnapshot              = VK_SNAPSHOT
	KeyInsert                = VK_INSERT
	KeyDelete                = VK_DELETE
	KeyHelp                  = VK_HELP
	Key0                     = 0x30
	Key1                     = 0x31
	Key2                     = 0x32
	Key3                     = 0x33
	Key4                     = 0x34
	Key5                     = 0x35
	Key6                     = 0x36
	Key7                     = 0x37
	Key8                     = 0x38
	Key9                     = 0x39
	KeyA                     = 0x41
	KeyB                     = 0x42
	KeyC                     = 0x43
	KeyD                     = 0x44
	KeyE                     = 0x45
	KeyF                     = 0x46
	KeyG                     = 0x47
	KeyH                     = 0x48
	KeyI                     = 0x49
	KeyJ                     = 0x4A
	KeyK                     = 0x4B
	KeyL                     = 0x4C
	KeyM                     = 0x4D
	KeyN                     = 0x4E
	KeyO                     = 0x4F
	KeyP                     = 0x50
	KeyQ                     = 0x51
	KeyR                     = 0x52
	KeyS                     = 0x53
	KeyT                     = 0x54
	KeyU                     = 0x55
	KeyV                     = 0x56
	KeyW                     = 0x57
	KeyX                     = 0x58
	KeyY                     = 0x59
	KeyZ                     = 0x5A
	KeyLWin                  = VK_LWIN
	KeyRWin                  = VK_RWIN
	KeyApps                  = VK_APPS
	KeySleep                 = VK_SLEEP
	KeyNumpad0               = VK_NUMPAD0
	KeyNumpad1               = VK_NUMPAD1
	KeyNumpad2               = VK_NUMPAD2
	KeyNumpad3               = VK_NUMPAD3
	KeyNumpad4               = VK_NUMPAD4
	KeyNumpad5               = VK_NUMPAD5
	KeyNumpad6               = VK_NUMPAD6
	KeyNumpad7               = VK_NUMPAD7
	KeyNumpad8               = VK_NUMPAD8
	KeyNumpad9               = VK_NUMPAD9
	KeyMultiply              = VK_MULTIPLY
	KeyAdd                   = VK_ADD
	KeySeparator             = VK_SEPARATOR
	KeySubtract              = VK_SUBTRACT
	KeyDecimal               = VK_DECIMAL
	KeyDivide                = VK_DIVIDE
	KeyF1                    = VK_F1
	KeyF2                    = VK_F2
	KeyF3                    = VK_F3
	KeyF4                    = VK_F4
	KeyF5                    = VK_F5
	KeyF6                    = VK_F6
	KeyF7                    = VK_F7
	KeyF8                    = VK_F8
	KeyF9                    = VK_F9
	KeyF10                   = VK_F10
	KeyF11                   = VK_F11
	KeyF12                   = VK_F12
	KeyF13                   = VK_F13
	KeyF14                   = VK_F14
	KeyF15                   = VK_F15
	KeyF16                   = VK_F16
	KeyF17                   = VK_F17
	KeyF18                   = VK_F18
	KeyF19                   = VK_F19
	KeyF20                   = VK_F20
	KeyF21                   = VK_F21
	KeyF22                   = VK_F22
	KeyF23                   = VK_F23
	KeyF24                   = VK_F24
	KeyNumlock               = VK_NUMLOCK
	KeyScroll                = VK_SCROLL
	KeyLShift                = VK_LSHIFT
	KeyRShift                = VK_RSHIFT
	KeyLControl              = VK_LCONTROL
	KeyRControl              = VK_RCONTROL
	KeyLMenu                 = VK_LMENU
	KeyRMenu                 = VK_RMENU
	KeyBrowserBack           = VK_BROWSER_BACK
	KeyBrowserForward        = VK_BROWSER_FORWARD
	KeyBrowserRefresh        = VK_BROWSER_REFRESH
	KeyBrowserStop           = VK_BROWSER_STOP
	KeyBrowserSearch         = VK_BROWSER_SEARCH
	KeyBrowserFavorites      = VK_BROWSER_FAVORITES
	KeyBrowserHome           = VK_BROWSER_HOME
	KeyVolumeMute            = VK_VOLUME_MUTE
	KeyVolumeDown            = VK_VOLUME_DOWN
	KeyVolumeUp              = VK_VOLUME_UP
	KeyMediaNextTrack        = VK_MEDIA_NEXT_TRACK
	KeyMediaPrevTrack        = VK_MEDIA_PREV_TRACK
	KeyMediaStop             = VK_MEDIA_STOP
	KeyMediaPlayPause        = VK_MEDIA_PLAY_PAUSE
	KeyLaunchMail            = VK_LAUNCH_MAIL
	KeyLaunchMediaSelect     = VK_LAUNCH_MEDIA_SELECT
	KeyLaunchApp1            = VK_LAUNCH_APP1
	KeyLaunchApp2            = VK_LAUNCH_APP2
	KeyOEM1                  = VK_OEM_1
	KeyOEMPlus               = VK_OEM_PLUS
	KeyOEMComma              = VK_OEM_COMMA
	KeyOEMMinus              = VK_OEM_MINUS
	KeyOEMPeriod             = VK_OEM_PERIOD
	KeyOEM2                  = VK_OEM_2
	KeyOEM3                  = VK_OEM_3
	KeyOEM4                  = VK_OEM_4
	KeyOEM5                  = VK_OEM_5
	KeyOEM6                  = VK_OEM_6
	KeyOEM7                  = VK_OEM_7
	KeyOEM8                  = VK_OEM_8
	KeyOEM102                = VK_OEM_102
	KeyProcessKey            = VK_PROCESSKEY
	KeyPacket                = VK_PACKET
	KeyAttn                  = VK_ATTN
	KeyCRSel                 = VK_CRSEL
	KeyEXSel                 = VK_EXSEL
	KeyErEOF                 = VK_EREOF
	KeyPlay                  = VK_PLAY
	KeyZoom                  = VK_ZOOM
	KeyNoName                = VK_NONAME
	KeyPA1                   = VK_PA1
	KeyOEMClear              = VK_OEM_CLEAR
)

var key2string = map[Key]string{
	KeyLButton:           "LButton",
	KeyRButton:           "RButton",
	KeyCancel:            "Cancel",
	KeyMButton:           "MButton",
	KeyXButton1:          "XButton1",
	KeyXButton2:          "XButton2",
	KeyBack:              "Back",
	KeyTab:               "Tab",
	KeyClear:             "Clear",
	KeyReturn:            "Return",
	KeyShift:             "Shift",
	KeyControl:           "Control",
	KeyMenu:              "Menu",
	KeyPause:             "Pause",
	KeyCapital:           "Capital",
	KeyKana:              "Kana / Hangul",
	KeyJunja:             "Junja",
	KeyFinal:             "Final",
	KeyHanja:             "Hanja / Kanji",
	KeyEscape:            "Escape",
	KeyConvert:           "Convert",
	KeyNonconvert:        "Nonconvert",
	KeyAccept:            "Accept",
	KeyModeChange:        "ModeChange",
	KeySpace:             "Space",
	KeyPrior:             "Prior",
	KeyNext:              "Next",
	KeyEnd:               "End",
	KeyHome:              "Home",
	KeyLeft:              "Left",
	KeyUp:                "Up",
	KeyRight:             "Right",
	KeyDown:              "Down",
	KeySelect:            "Select",
	KeyPrint:             "Print",
	KeyExecute:           "Execute",
	KeySnapshot:          "Snapshot",
	KeyInsert:            "Insert",
	KeyDelete:            "Delete",
	KeyHelp:              "Help",
	Key0:                 "0",
	Key1:                 "1",
	Key2:                 "2",
	Key3:                 "3",
	Key4:                 "4",
	Key5:                 "5",
	Key6:                 "6",
	Key7:                 "7",
	Key8:                 "8",
	Key9:                 "9",
	KeyA:                 "A",
	KeyB:                 "B",
	KeyC:                 "C",
	KeyD:                 "D",
	KeyE:                 "E",
	KeyF:                 "F",
	KeyG:                 "G",
	KeyH:                 "H",
	KeyI:                 "I",
	KeyJ:                 "J",
	KeyK:                 "K",
	KeyL:                 "L",
	KeyM:                 "M",
	KeyN:                 "N",
	KeyO:                 "O",
	KeyP:                 "P",
	KeyQ:                 "Q",
	KeyR:                 "R",
	KeyS:                 "S",
	KeyT:                 "T",
	KeyU:                 "U",
	KeyV:                 "V",
	KeyW:                 "W",
	KeyX:                 "X",
	KeyY:                 "Y",
	KeyZ:                 "Z",
	KeyLWin:              "LWin",
	KeyRWin:              "RWin",
	KeyApps:              "Apps",
	KeySleep:             "Sleep",
	KeyNumpad0:           "Numpad0",
	KeyNumpad1:           "Numpad1",
	KeyNumpad2:           "Numpad2",
	KeyNumpad3:           "Numpad3",
	KeyNumpad4:           "Numpad4",
	KeyNumpad5:           "Numpad5",
	KeyNumpad6:           "Numpad6",
	KeyNumpad7:           "Numpad7",
	KeyNumpad8:           "Numpad8",
	KeyNumpad9:           "Numpad9",
	KeyMultiply:          "Multiply",
	KeyAdd:               "Add",
	KeySeparator:         "Separator",
	KeySubtract:          "Subtract",
	KeyDecimal:           "Decimal",
	KeyDivide:            "Divide",
	KeyF1:                "F1",
	KeyF2:                "F2",
	KeyF3:                "F3",
	KeyF4:                "F4",
	KeyF5:                "F5",
	KeyF6:                "F6",
	KeyF7:                "F7",
	KeyF8:                "F8",
	KeyF9:                "F9",
	KeyF10:               "F10",
	KeyF11:               "F11",
	KeyF12:               "F12",
	KeyF13:               "F13",
	KeyF14:               "F14",
	KeyF15:               "F15",
	KeyF16:               "F16",
	KeyF17:               "F17",
	KeyF18:               "F18",
	KeyF19:               "F19",
	KeyF20:               "F20",
	KeyF21:               "F21",
	KeyF22:               "F22",
	KeyF23:               "F23",
	KeyF24:               "F24",
	KeyNumlock:           "Numlock",
	KeyScroll:            "Scroll",
	KeyLShift:            "LShift",
	KeyRShift:            "RShift",
	KeyLControl:          "LControl",
	KeyRControl:          "RControl",
	KeyLMenu:             "LMenu",
	KeyRMenu:             "RMenu",
	KeyBrowserBack:       "BrowserBack",
	KeyBrowserForward:    "BrowserForward",
	KeyBrowserRefresh:    "BrowserRefresh",
	KeyBrowserStop:       "BrowserStop",
	KeyBrowserSearch:     "BrowserSearch",
	KeyBrowserFavorites:  "BrowserFavorites",
	KeyBrowserHome:       "BrowserHome",
	KeyVolumeMute:        "VolumeMute",
	KeyVolumeDown:        "VolumeDown",
	KeyVolumeUp:          "VolumeUp",
	KeyMediaNextTrack:    "MediaNextTrack",
	KeyMediaPrevTrack:    "MediaPrevTrack",
	KeyMediaStop:         "MediaStop",
	KeyMediaPlayPause:    "MediaPlayPause",
	KeyLaunchMail:        "LaunchMail",
	KeyLaunchMediaSelect: "LaunchMediaSelect",
	KeyLaunchApp1:        "LaunchApp1",
	KeyLaunchApp2:        "LaunchApp2",
	KeyOEM1:              "OEM1",
	KeyOEMPlus:           "OEMPlus",
	KeyOEMComma:          "OEMComma",
	KeyOEMMinus:          "OEMMinus",
	KeyOEMPeriod:         "OEMPeriod",
	KeyOEM2:              "OEM2",
	KeyOEM3:              "OEM3",
	KeyOEM4:              "OEM4",
	KeyOEM5:              "OEM5",
	KeyOEM6:              "OEM6",
	KeyOEM7:              "OEM7",
	KeyOEM8:              "OEM8",
	KeyOEM102:            "OEM102",
	KeyProcessKey:        "ProcessKey",
	KeyPacket:            "Packet",
	KeyAttn:              "Attn",
	KeyCRSel:             "CRSel",
	KeyEXSel:             "EXSel",
	KeyErEOF:             "ErEOF",
	KeyPlay:              "Play",
	KeyZoom:              "Zoom",
	KeyNoName:            "NoName",
	KeyPA1:               "PA1",
	KeyOEMClear:          "OEMClear",
}

func (k Key) String() string {
	return key2string[k]
}

func AltDown() bool {
	return GetKeyState(KeyMenu)>>15 != 0
}

func ControlDown() bool {
	return GetKeyState(KeyControl)>>15 != 0
}

func ShiftDown() bool {
	return GetKeyState(KeyShift)>>15 != 0
}
<commit_msg>Add KeyAlt as alternative to KeyMenu<commit_after>// Copyright 2013 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	. "github.com/lxn/go-winapi"
)

type Key uint16

const (
	KeyLButton           Key = VK_LBUTTON
	KeyRButton               = VK_RBUTTON
	KeyCancel                = VK_CANCEL
	KeyMButton               = VK_MBUTTON
	KeyXButton1              = VK_XBUTTON1
	KeyXButton2              = VK_XBUTTON2
	KeyBack                  = VK_BACK
	KeyTab                   = VK_TAB
	KeyClear                 = VK_CLEAR
	KeyReturn                = VK_RETURN
	KeyShift                 = VK_SHIFT
	KeyControl               = VK_CONTROL
	KeyAlt                   = VK_MENU
	KeyMenu                  = VK_MENU
	KeyPause                 = VK_PAUSE
	KeyCapital               = VK_CAPITAL
	KeyKana                  = VK_KANA
	KeyHangul                = VK_HANGUL
	KeyJunja                 = VK_JUNJA
	KeyFinal                 = VK_FINAL
	KeyHanja                 = VK_HANJA
	KeyKanji                 = VK_KANJI
	KeyEscape                = VK_ESCAPE
	KeyConvert               = VK_CONVERT
	KeyNonconvert            = VK_NONCONVERT
	KeyAccept                = VK_ACCEPT
	KeyModeChange            = VK_MODECHANGE
	KeySpace                 = VK_SPACE
	KeyPrior                 = VK_PRIOR
	KeyNext                  = VK_NEXT
	KeyEnd                   = VK_END
	KeyHome                  = VK_HOME
	KeyLeft                  = VK_LEFT
	KeyUp                    = VK_UP
	KeyRight                 = VK_RIGHT
	KeyDown                  = VK_DOWN
	KeySelect                = VK_SELECT
	KeyPrint                 = VK_PRINT
	KeyExecute               = VK_EXECUTE
	KeySnapshot              = VK_SNAPSHOT
	KeyInsert                = VK_INSERT
	KeyDelete                = VK_DELETE
	KeyHelp                  = VK_HELP
	Key0                     = 0x30
	Key1                     = 0x31
	Key2                     = 0x32
	Key3                     = 0x33
	Key4                     = 0x34
	Key5                     = 0x35
	Key6                     = 0x36
	Key7                     = 0x37
	Key8                     = 0x38
	Key9                     = 0x39
	KeyA                     = 0x41
	KeyB                     = 0x42
	KeyC                     = 0x43
	KeyD                     = 0x44
	KeyE                     = 0x45
	KeyF                     = 0x46
	KeyG                     = 0x47
	KeyH                     = 0x48
	KeyI                     = 0x49
	KeyJ                     = 0x4A
	KeyK                     = 0x4B
	KeyL                     = 0x4C
	KeyM                     = 0x4D
	KeyN                     = 0x4E
	KeyO                     = 0x4F
	KeyP                     = 0x50
	KeyQ                     = 0x51
	KeyR                     = 0x52
	KeyS                     = 0x53
	KeyT                     = 0x54
	KeyU                     = 0x55
	KeyV                     = 0x56
	KeyW                     = 0x57
	KeyX                     = 0x58
	KeyY                     = 0x59
	KeyZ                     = 0x5A
	KeyLWin                  = VK_LWIN
	KeyRWin                  = VK_RWIN
	KeyApps                  = VK_APPS
	KeySleep                 = VK_SLEEP
	KeyNumpad0               = VK_NUMPAD0
	KeyNumpad1               = VK_NUMPAD1
	KeyNumpad2               = VK_NUMPAD2
	KeyNumpad3               = VK_NUMPAD3
	KeyNumpad4               = VK_NUMPAD4
	KeyNumpad5               = VK_NUMPAD5
	KeyNumpad6               = VK_NUMPAD6
	KeyNumpad7               = VK_NUMPAD7
	KeyNumpad8               = VK_NUMPAD8
	KeyNumpad9               = VK_NUMPAD9
	KeyMultiply              = VK_MULTIPLY
	KeyAdd                   = VK_ADD
	KeySeparator             = VK_SEPARATOR
	KeySubtract              = VK_SUBTRACT
	KeyDecimal               = VK_DECIMAL
	KeyDivide                = VK_DIVIDE
	KeyF1                    = VK_F1
	KeyF2                    = VK_F2
	KeyF3                    = VK_F3
	KeyF4                    = VK_F4
	KeyF5                    = VK_F5
	KeyF6                    = VK_F6
	KeyF7                    = VK_F7
	KeyF8                    = VK_F8
	KeyF9                    = VK_F9
	KeyF10                   = VK_F10
	KeyF11                   = VK_F11
	KeyF12                   = VK_F12
	KeyF13                   = VK_F13
	KeyF14                   = VK_F14
	KeyF15                   = VK_F15
	KeyF16                   = VK_F16
	KeyF17                   = VK_F17
	KeyF18                   = VK_F18
	KeyF19                   = VK_F19
	KeyF20                   = VK_F20
	KeyF21                   = VK_F21
	KeyF22                   = VK_F22
	KeyF23                   = VK_F23
	KeyF24                   = VK_F24
	KeyNumlock               = VK_NUMLOCK
	KeyScroll                = VK_SCROLL
	KeyLShift                = VK_LSHIFT
	KeyRShift                = VK_RSHIFT
	KeyLControl              = VK_LCONTROL
	KeyRControl              = VK_RCONTROL
	KeyLMenu                 = VK_LMENU
	KeyRMenu                 = VK_RMENU
	KeyBrowserBack           = VK_BROWSER_BACK
	KeyBrowserForward        = VK_BROWSER_FORWARD
	KeyBrowserRefresh        = VK_BROWSER_REFRESH
	KeyBrowserStop           = VK_BROWSER_STOP
	KeyBrowserSearch         = VK_BROWSER_SEARCH
	KeyBrowserFavorites      = VK_BROWSER_FAVORITES
	KeyBrowserHome           = VK_BROWSER_HOME
	KeyVolumeMute            = VK_VOLUME_MUTE
	KeyVolumeDown            = VK_VOLUME_DOWN
	KeyVolumeUp              = VK_VOLUME_UP
	KeyMediaNextTrack        = VK_MEDIA_NEXT_TRACK
	KeyMediaPrevTrack        = VK_MEDIA_PREV_TRACK
	KeyMediaStop             = VK_MEDIA_STOP
	KeyMediaPlayPause        = VK_MEDIA_PLAY_PAUSE
	KeyLaunchMail            = VK_LAUNCH_MAIL
	KeyLaunchMediaSelect     = VK_LAUNCH_MEDIA_SELECT
	KeyLaunchApp1            = VK_LAUNCH_APP1
	KeyLaunchApp2            = VK_LAUNCH_APP2
	KeyOEM1                  = VK_OEM_1
	KeyOEMPlus               = VK_OEM_PLUS
	KeyOEMComma              = VK_OEM_COMMA
	KeyOEMMinus              = VK_OEM_MINUS
	KeyOEMPeriod             = VK_OEM_PERIOD
	KeyOEM2                  = VK_OEM_2
	KeyOEM3                  = VK_OEM_3
	KeyOEM4                  = VK_OEM_4
	KeyOEM5                  = VK_OEM_5
	KeyOEM6                  = VK_OEM_6
	KeyOEM7                  = VK_OEM_7
	KeyOEM8                  = VK_OEM_8
	KeyOEM102                = VK_OEM_102
	KeyProcessKey            = VK_PROCESSKEY
	KeyPacket                = VK_PACKET
	KeyAttn                  = VK_ATTN
	KeyCRSel                 = VK_CRSEL
	KeyEXSel                 = VK_EXSEL
	KeyErEOF                 = VK_EREOF
	KeyPlay                  = VK_PLAY
	KeyZoom                  = VK_ZOOM
	KeyNoName                = VK_NONAME
	KeyPA1                   = VK_PA1
	KeyOEMClear              = VK_OEM_CLEAR
)

var key2string = map[Key]string{
	KeyLButton:           "LButton",
	KeyRButton:           "RButton",
	KeyCancel:            "Cancel",
	KeyMButton:           "MButton",
	KeyXButton1:          "XButton1",
	KeyXButton2:          "XButton2",
	KeyBack:              "Back",
	KeyTab:               "Tab",
	KeyClear:             "Clear",
	KeyReturn:            "Return",
	KeyShift:             "Shift",
	KeyControl:           "Control",
	KeyAlt:               "Alt / Menu",
	KeyPause:             "Pause",
	KeyCapital:           "Capital",
	KeyKana:              "Kana / Hangul",
	KeyJunja:             "Junja",
	KeyFinal:             "Final",
	KeyHanja:             "Hanja / Kanji",
	KeyEscape:            "Escape",
	KeyConvert:           "Convert",
	KeyNonconvert:        "Nonconvert",
	KeyAccept:            "Accept",
	KeyModeChange:        "ModeChange",
	KeySpace:             "Space",
	KeyPrior:             "Prior",
	KeyNext:              "Next",
	KeyEnd:               "End",
	KeyHome:              "Home",
	KeyLeft:              "Left",
	KeyUp:                "Up",
	KeyRight:             "Right",
	KeyDown:              "Down",
	KeySelect:            "Select",
	KeyPrint:             "Print",
	KeyExecute:           "Execute",
	KeySnapshot:          "Snapshot",
	KeyInsert:            "Insert",
	KeyDelete:            "Delete",
	KeyHelp:              "Help",
	Key0:                 "0",
	Key1:                 "1",
	Key2:                 "2",
	Key3:                 "3",
	Key4:                 "4",
	Key5:                 "5",
	Key6:                 "6",
	Key7:                 "7",
	Key8:                 "8",
	Key9:                 "9",
	KeyA:                 "A",
	KeyB:                 "B",
	KeyC:                 "C",
	KeyD:                 "D",
	KeyE:                 "E",
	KeyF:                 "F",
	KeyG:                 "G",
	KeyH:                 "H",
	KeyI:                 "I",
	KeyJ:                 "J",
	KeyK:                 "K",
	KeyL:                 "L",
	KeyM:                 "M",
	KeyN:                 "N",
	KeyO:                 "O",
	KeyP:                 "P",
	KeyQ:                 "Q",
	KeyR:                 "R",
	KeyS:                 "S",
	KeyT:                 "T",
	KeyU:                 "U",
	KeyV:                 "V",
	KeyW:                 "W",
	KeyX:                 "X",
	KeyY:                 "Y",
	KeyZ:                 "Z",
	KeyLWin:              "LWin",
	KeyRWin:              "RWin",
	KeyApps:              "Apps",
	KeySleep:             "Sleep",
	KeyNumpad0:           "Numpad0",
	KeyNumpad1:           "Numpad1",
	KeyNumpad2:           "Numpad2",
	KeyNumpad3:           "Numpad3",
	KeyNumpad4:           "Numpad4",
	KeyNumpad5:           "Numpad5",
	KeyNumpad6:           "Numpad6",
	KeyNumpad7:           "Numpad7",
	KeyNumpad8:           "Numpad8",
	KeyNumpad9:           "Numpad9",
	KeyMultiply:          "Multiply",
	KeyAdd:               "Add",
	KeySeparator:         "Separator",
	KeySubtract:          "Subtract",
	KeyDecimal:           "Decimal",
	KeyDivide:            "Divide",
	KeyF1:                "F1",
	KeyF2:                "F2",
	KeyF3:                "F3",
	KeyF4:                "F4",
	KeyF5:                "F5",
	KeyF6:                "F6",
	KeyF7:                "F7",
	KeyF8:                "F8",
	KeyF9:                "F9",
	KeyF10:               "F10",
	KeyF11:               "F11",
	KeyF12:               "F12",
	KeyF13:               "F13",
	KeyF14:               "F14",
	KeyF15:               "F15",
	KeyF16:               "F16",
	KeyF17:               "F17",
	KeyF18:               "F18",
	KeyF19:               "F19",
	KeyF20:               "F20",
	KeyF21:               "F21",
	KeyF22:               "F22",
	KeyF23:               "F23",
	KeyF24:               "F24",
	KeyNumlock:           "Numlock",
	KeyScroll:            "Scroll",
	KeyLShift:            "LShift",
	KeyRShift:            "RShift",
	KeyLControl:          "LControl",
	KeyRControl:          "RControl",
	KeyLMenu:             "LMenu",
	KeyRMenu:             "RMenu",
	KeyBrowserBack:       "BrowserBack",
	KeyBrowserForward:    "BrowserForward",
	KeyBrowserRefresh:    "BrowserRefresh",
	KeyBrowserStop:       "BrowserStop",
	KeyBrowserSearch:     "BrowserSearch",
	KeyBrowserFavorites:  "BrowserFavorites",
	KeyBrowserHome:       "BrowserHome",
	KeyVolumeMute:        "VolumeMute",
	KeyVolumeDown:        "VolumeDown",
	KeyVolumeUp:          "VolumeUp",
	KeyMediaNextTrack:    "MediaNextTrack",
	KeyMediaPrevTrack:    "MediaPrevTrack",
	KeyMediaStop:         "MediaStop",
	KeyMediaPlayPause:    "MediaPlayPause",
	KeyLaunchMail:        "LaunchMail",
	KeyLaunchMediaSelect: "LaunchMediaSelect",
	KeyLaunchApp1:        "LaunchApp1",
	KeyLaunchApp2:        "LaunchApp2",
	KeyOEM1:              "OEM1",
	KeyOEMPlus:           "OEMPlus",
	KeyOEMComma:          "OEMComma",
	KeyOEMMinus:          "OEMMinus",
	KeyOEMPeriod:         "OEMPeriod",
	KeyOEM2:              "OEM2",
	KeyOEM3:              "OEM3",
	KeyOEM4:              "OEM4",
	KeyOEM5:              "OEM5",
	KeyOEM6:              "OEM6",
	KeyOEM7:              "OEM7",
	KeyOEM8:              "OEM8",
	KeyOEM102:            "OEM102",
	KeyProcessKey:        "ProcessKey",
	KeyPacket:            "Packet",
	KeyAttn:              "Attn",
	KeyCRSel:             "CRSel",
	KeyEXSel:             "EXSel",
	KeyErEOF:             "ErEOF",
	KeyPlay:              "Play",
	KeyZoom:              "Zoom",
	KeyNoName:            "NoName",
	KeyPA1:               "PA1",
	KeyOEMClear:          "OEMClear",
}

func (k Key) String() string {
	return key2string[k]
}

func AltDown() bool {
	return GetKeyState(KeyAlt)>>15 != 0
}

func ControlDown() bool {
	return GetKeyState(KeyControl)>>15 != 0
}

func ShiftDown() bool {
	return GetKeyState(KeyShift)>>15 != 0
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/couchbaselabs/indexing/btree"
    "os"
    "time"
)

var _ = fmt.Sprintln("keep 'fmt' import during debugging", time.Now())

func main() {
    idxfile, kvfile := "./data/test_insert_index.dat", "./data/test_insert_kv.dat"
    os.Remove(idxfile)
    os.Remove(kvfile)

    var conf = btree.Config{
        Idxfile: idxfile,
        Kvfile:  kvfile,
        IndexConfig: btree.IndexConfig{
            Sectorsize: 512,
            Flistsize:  1000 * btree.OFFSET_SIZE,
            Blocksize:  4 * 1024,
        },
        Maxlevel:      6,
        RebalanceThrs: 25,
        AppendRatio:   0.7,
        DrainRate:     200,
        MaxLeafCache:  1000,
        Sync:          false,
        Nocache:       false,
    }
    bt := btree.NewBTree(btree.NewStore(conf))

    seed := time.Now().UnixNano()
    fmt.Println("Seed:", seed)
    doinsert(seed, 100, 10000, bt, false)
    bt.Drain()
    bt.Stats(true)
    fmt.Println()
    bt.Close()
}

func doinsert(seed int64, factor, count int, bt *btree.BTree, check bool) {
    keys, values := btree.TestData(count, seed)
    for i := 0; i < factor; i++ {
        for j := 0; j < count; j++ {
            k, v := keys[j], values[j]
            k.Id = (i * count) + j
            bt.Insert(k, v)
            if check {
                bt.Drain()
                bt.Check()
            }
        }
        fmt.Println("Done ", time.Now().UnixNano()/1000000, (i+1)*count)
    }
}
<commit_msg>Adjusted test_insert.go to cummulatively insert millions of entries.<commit_after>package main

import (
    "fmt"
    "github.com/couchbaselabs/indexing/btree"
    //"os"
    "time"
)

var _ = fmt.Sprintln("keep 'fmt' import during debugging", time.Now())

func main() {
    idxfile, kvfile := "./data/test_insert_index.dat", "./data/test_insert_kv.dat"
    //os.Remove(idxfile)
    //os.Remove(kvfile)

    var conf = btree.Config{
        Idxfile: idxfile,
        Kvfile:  kvfile,
        IndexConfig: btree.IndexConfig{
            Sectorsize: 512,
            Flistsize:  1000 * btree.OFFSET_SIZE,
            Blocksize:  4 * 1024,
        },
        Maxlevel:      6,
        RebalanceThrs: 25,
        AppendRatio:   0.7,
        DrainRate:     200,
        MaxLeafCache:  1000,
        Sync:          false,
        Nocache:       false,
    }
    bt := btree.NewBTree(btree.NewStore(conf))

    seed := time.Now().UnixNano()
    fmt.Println("Seed:", seed)
    doinsert(seed, 10, 10000, bt, false)
    bt.Drain()
    bt.Stats(true)
    fmt.Println()
    bt.Close()
}

func doinsert(seed int64, factor, count int, bt *btree.BTree, check bool) {
    keys, values := btree.TestData(count, seed)
    for i := 0; i < factor; i++ {
        for j := 0; j < count; j++ {
            k, v := keys[j], values[j]
            k.Id = (i * count) + j
            bt.Insert(k, v)
            if check {
                bt.Drain()
                bt.Check()
            }
        }
        fmt.Println("Done ", time.Now().UnixNano()/1000000, (i+1)*count)
    }
}
<|endoftext|>