<commit_before>package bsdecoder

import (
	//	"errors"
	"io"

	"fmt"
	"github.com/pkg/errors"
)

type ByteOrder interface {
	Uint8([]byte, uint, uint) (uint8, error)
	// Uint16([]byte, uint, uint) (uint16, error)
	// Uint32([]byte, uint, uint) (uint32, error)
	// Uint64([]byte, uint, uint) (uint64, error)
	// PutUint8([]byte, uint8)
	// PutUint16([]byte, uint16)
	// PutUint32([]byte, uint32)
	// PutUint64([]byte, uint64)
	// String() string
}

var LittleEndian littleEndian
var BigEndian bigEndian

type littleEndian struct{}

func (littleEndian) Uint8(b []byte, o uint, l uint) (uint8, error) {
	if o == 0 && l == 8 {
		return b[0], nil
	}
	return 0, nil
}

type bigEndian struct{}

// order: bsdecoder.LittleEndian
// data: pointer to data that will be output
// length: specify read length in bit
func Read(r io.Reader, order ByteOrder, data interface{}, length int) error {
	s := maxDataSize(data)
	if s == 0 {
		return errors.New("given type is not supported.")
	}
	if s < length {
		return errors.New(fmt.Sprintf("Given length (0x%x) is longer than given data type.", length))
	}

	fmt.Printf("size of data: %d bit(s).\n", s)

	if s%8 == 0 {
		// fast path: just read by io.ReadFull
		var b [8]byte
		bs := b[:(s / 8)]
		if _, err := io.ReadFull(r, bs); err != nil {
			return errors.Wrap(err, "Failed to read by io.ReadFull")
		}

		switch data := data.(type) {
		case *uint8:
			var err error
			*data, err = order.Uint8(bs, 0, 8)
			if err != nil {
				return errors.Wrap(err, "Failed to convert to uint8")
			}
		default:
			return errors.New("Other than uint8 is not supported for now.")
		}

		return nil

	} else {
		return read(r, order, data, length)
	}

	return nil
}

func read(r io.Reader, order ByteOrder, data interface{}, length int) error {
	return nil
}

// returns size in BIT.
func maxDataSize(data interface{}) int {
	switch data := data.(type) {
	case bool, int8, uint8, *bool, *int8, *uint8:
		return 8
	case []int8:
		return 8 * len(data)
	case []uint8:
		return 8 * len(data)
	case int16, uint16, *int16, *uint16:
		return 16
	case []int16:
		return 16 * len(data)
	case []uint16:
		return 16 * len(data)
	case int32, uint32, *int32, *uint32:
		return 32
	case []int32:
		return 32 * len(data)
	case []uint32:
		return 32 * len(data)
	case int64, uint64, *int64, *uint64:
		return 64
	case []int64:
		return 64 * len(data)
	case []uint64:
		return 64 * len(data)
	}
	return 0
}
<commit_msg>create instance by NewReader<commit_after>package bsio

import (
	//	"errors"
	"io"

	"fmt"
	"github.com/pkg/errors"
)

type ByteOrder interface {
	Uint8([]byte, uint, uint) (uint8, error)
	// Uint16([]byte, uint, uint) (uint16, error)
	// Uint32([]byte, uint, uint) (uint32, error)
	// Uint64([]byte, uint, uint) (uint64, error)
	// PutUint8([]byte, uint8)
	// PutUint16([]byte, uint16)
	// PutUint32([]byte, uint32)
	// PutUint64([]byte, uint64)
	// String() string
}

var LittleEndian littleEndian
var BigEndian bigEndian

type littleEndian struct{}

func (littleEndian) Uint8(b []byte, o uint, l uint) (uint8, error) {
	if o == 0 && l == 8 {
		return b[0], nil
	}
	return 0, nil
}

type bigEndian struct{}

type Reader struct {
	buf   []byte
	rd    io.Reader
	order ByteOrder
	rp    int  // read pointer
	last  byte // last 1-byte data
}

func NewReader(r io.Reader, order ByteOrder) *Reader {
	b := new(Reader)
	b.rd = r
	b.order = order
	return b
}

// order: bsdecoder.LittleEndian
// data: pointer to data that will be output
// length: specify read length in bit
func (this *Reader) Read(data interface{}, length int) error {
	s := maxDataSize(data)
	if s == 0 {
		return errors.New("given type is not supported.")
	}
	if s < length {
		return errors.New(fmt.Sprintf("Given length (0x%x) is longer than given data type.", length))
	}

	fmt.Printf("size of data: %d bit(s).\n", s)

	if s%8 == 0 {
		// fast path: just read by io.ReadFull
		var b [8]byte
		bs := b[:(s / 8)]
		if _, err := io.ReadFull(this.rd, bs); err != nil {
			return errors.Wrap(err, "Failed to read by io.ReadFull")
		}

		switch data := data.(type) {
		case *uint8:
			var err error
			*data, err = this.order.Uint8(bs, 0, 8)
			if err != nil {
				return errors.Wrap(err, "Failed to convert to uint8")
			}
		default:
			return errors.New("Other than uint8 is not supported for now.")
		}

		return nil

	} else {
		return this.read(data, length)
	}

	return nil
}

func (this *Reader) read(data interface{}, length int) error {
	return nil
}

// returns size in BIT.
func maxDataSize(data interface{}) int {
	switch data := data.(type) {
	case bool, int8, uint8, *bool, *int8, *uint8:
		return 8
	case []int8:
		return 8 * len(data)
	case []uint8:
		return 8 * len(data)
	case int16, uint16, *int16, *uint16:
		return 16
	case []int16:
		return 16 * len(data)
	case []uint16:
		return 16 * len(data)
	case int32, uint32, *int32, *uint32:
		return 32
	case []int32:
		return 32 * len(data)
	case []uint32:
		return 32 * len(data)
	case int64, uint64, *int64, *uint64:
		return 64
	case []int64:
		return 64 * len(data)
	case []uint64:
		return 64 * len(data)
	}
	return 0
}
<|endoftext|>


<commit_before>package gst

/*
#include <stdlib.h>
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>

int capsRefCount(GstCaps *c) {
	return GST_CAPS_REFCOUNT(c);
}
*/
import "C"

import (
	"github.com/lidouf/glib"
	"unsafe"
)

//type Caps C.GstCaps
type Caps struct {
	glib.Object
}

func (c *Caps) g() *C.GstCaps {
	return (*C.GstCaps)(c.GetPtr())
}

func (c *Caps) Ref() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_ref(c.g())))
	return r
}

func (c *Caps) Unref() {
	C.gst_caps_unref(c.g())
}

func (c *Caps) RefCount() int {
	return int(C.capsRefCount(c.g()))
}

func (c *Caps) AppendStructure(media_type string, fields glib.Params) {
	C.gst_caps_append_structure(c.g(), makeGstStructure(media_type, fields))
}

func (c *Caps) GetSize() int {
	return int(C.gst_caps_get_size(c.g()))
}

func (c *Caps) GetStructure(index uint) *Structure {
	return (*Structure)(C.gst_caps_get_structure(c.g(), C.guint(index)))
}

func (c *Caps) String() string {
	s := (*C.char)(C.gst_caps_to_string(c.g()))
	defer C.free(unsafe.Pointer(s))
	return C.GoString(s)
}

func (c *Caps) IsAny() bool {
	return C.gst_caps_is_any(c.g()) != 0
}

func (c *Caps) IsEmpty() bool {
	return C.gst_caps_is_empty(c.g()) != 0
}

func (c *Caps) IsFixed() bool {
	return C.gst_caps_is_fixed(c.g()) != 0
}

func (c *Caps) GetCodecDescription() string {
	return C.GoString((*C.char)(C.gst_pb_utils_get_codec_description(c.g())))
}

func NewCapsAny() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_new_any()))
	return r
}

func NewCapsEmpty() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_new_empty()))
	return r
}

func NewCapsSimple(media_type string, fields glib.Params) *Caps {
	c := NewCapsEmpty()
	c.AppendStructure(media_type, fields)
	return c
}

func CapsFromString(s string) *Caps {
	cs := (*C.gchar)(C.CString(s))
	defer C.free(unsafe.Pointer(cs))
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_from_string(cs)))
	return r
}

//type StaticCaps C.GstStaticCaps
type StaticCaps struct {
	glib.Object
}

func (c *StaticCaps) g() *C.GstStaticCaps {
	return (*C.GstStaticCaps)(c.GetPtr())
}

func (c *StaticCaps) Caps() *Caps {
	if c.g().caps == nil {
		return nil
	}
	r := new(Caps)
	r.SetPtr(glib.Pointer(c.g().caps))
	return r
}

func (c *StaticCaps) Get() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_static_caps_get(c.g())))
	return r
}

func (c *StaticCaps) String() string {
	return C.GoString(c.g().string)
}
<commit_msg>add Type() & Value() implementation for GstCaps<commit_after>package gst

/*
#include <stdlib.h>
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>

int capsRefCount(GstCaps *c) {
	return GST_CAPS_REFCOUNT(c);
}
*/
import "C"

import (
	"github.com/lidouf/glib"
	"unsafe"
)

//type Caps C.GstCaps
type Caps struct {
	glib.Object
}

func (c *Caps) Type() glib.Type {
	return glib.TypeFromName("GstCaps")
}

func (c *Caps) Value() *glib.Value {
	v := glib.NewValue(c.Type())
	C.gst_value_set_caps(v2g(v), c.g())
	return v
}

func (c *Caps) g() *C.GstCaps {
	return (*C.GstCaps)(c.GetPtr())
}

func (c *Caps) Ref() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_ref(c.g())))
	return r
}

func (c *Caps) Unref() {
	C.gst_caps_unref(c.g())
}

func (c *Caps) RefCount() int {
	return int(C.capsRefCount(c.g()))
}

func (c *Caps) AppendStructure(media_type string, fields glib.Params) {
	C.gst_caps_append_structure(c.g(), makeGstStructure(media_type, fields))
}

func (c *Caps) GetSize() int {
	return int(C.gst_caps_get_size(c.g()))
}

func (c *Caps) GetStructure(index uint) *Structure {
	return (*Structure)(C.gst_caps_get_structure(c.g(), C.guint(index)))
}

func (c *Caps) String() string {
	s := (*C.char)(C.gst_caps_to_string(c.g()))
	defer C.free(unsafe.Pointer(s))
	return C.GoString(s)
}

func (c *Caps) IsAny() bool {
	return C.gst_caps_is_any(c.g()) != 0
}

func (c *Caps) IsEmpty() bool {
	return C.gst_caps_is_empty(c.g()) != 0
}

func (c *Caps) IsFixed() bool {
	return C.gst_caps_is_fixed(c.g()) != 0
}

func (c *Caps) GetCodecDescription() string {
	return C.GoString((*C.char)(C.gst_pb_utils_get_codec_description(c.g())))
}

func NewCapsAny() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_new_any()))
	return r
}

func NewCapsEmpty() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_new_empty()))
	return r
}

func NewCapsSimple(media_type string, fields glib.Params) *Caps {
	c := NewCapsEmpty()
	c.AppendStructure(media_type, fields)
	return c
}

func CapsFromString(s string) *Caps {
	cs := (*C.gchar)(C.CString(s))
	defer C.free(unsafe.Pointer(cs))
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_caps_from_string(cs)))
	return r
}

//type StaticCaps C.GstStaticCaps
type StaticCaps struct {
	glib.Object
}

func (c *StaticCaps) g() *C.GstStaticCaps {
	return (*C.GstStaticCaps)(c.GetPtr())
}

func (c *StaticCaps) Caps() *Caps {
	if c.g().caps == nil {
		return nil
	}
	r := new(Caps)
	r.SetPtr(glib.Pointer(c.g().caps))
	return r
}

func (c *StaticCaps) Get() *Caps {
	r := new(Caps)
	r.SetPtr(glib.Pointer(C.gst_static_caps_get(c.g())))
	return r
}

func (c *StaticCaps) String() string {
	return C.GoString(c.g().string)
}
<|endoftext|>


<commit_before>// Package vcard implements the vCard format, defined in RFC 6350.
package vcard

import (
	"strconv"
	"strings"
	"time"
)

const timestampLayout = "20060102T150405Z"

// Card property parameters.
const (
	ParamLanguage = "LANGUAGE"
	ParamValue = "VALUE"
	ParamPreferred = "PREF"
	ParamAltID = "ALTID"
	ParamPID = "PID"
	ParamType = "TYPE"
	ParamMediaType = "MEDIATYPE"
	ParamCalendarScale = "CALSCALE"
	ParamSortAs = "SORT-AS"
	ParamGeo = "GEO"
	ParamTimezone = "TZ"
)

// Card properties.
const (
	// General Properties
	FieldSource = "SOURCE"
	FieldKind = "KIND"
	FieldXML = "XML"

	// Identification Properties
	FieldFormattedName = "FN"
	FieldName = "N"
	FieldNickname = "NICKNAME"
	FieldPhoto = "PHOTO"
	FieldBirthday = "BDAY"
	FieldAnniversary = "ANIVERSARY"
	FieldGender = "GENDER"

	// Delivery Addressing Properties
	FieldAddress = "ADR"

	// Communications Properties
	FieldTelephone = "TEL"
	FieldEmail = "EMAIL"
	FieldIMPP = "IMPP" // Instant Messaging and Presence Protocol
	FieldLanguage = "LANG"

	// Geographical Properties
	FieldTimezone = "TZ"
	FieldGeo = "GEO"

	// Organizational Properties
	FieldTitle = "TITLE"
	FieldRole = "ROLE"
	FieldLogo = "LOGO"
	FieldOrganization = "ORG"
	FieldMember = "MEMBER"
	FieldRelated = "RELATED"

	// Explanatory Properties
	FieldCategories = "CATEGORIES"
	FieldNote = "NOTE"
	FieldProductID = "PRODID"
	FieldRevision = "REV"
	FieldSound = "SOUND"
	FieldUID = "UID"
	FieldClientPIDMap = "CLIENTPIDMAP"
	FieldURL = "URL"
	FieldVersion = "VERSION"

	// Security Properties
	FieldKey = "KEY"

	// Calendar Properties
	FieldFBURL = "FBURL"
	FieldCalendarAddressURI = "CALADRURI"
	FieldCalendarURI = "CALURI"
)

func maybeGet(l []string, i int) string {
	if i < len(l) {
		return l[i]
	}
	return ""
}

// A Card is an address book entry.
type Card map[string][]*Field

// Get returns the first field of the card for the given property. If there is
// no such field, it returns nil.
func (c Card) Get(k string) *Field {
	fields := c[k]
	if len(fields) == 0 {
		return nil
	}
	return fields[0]
}

// Preferred returns the preferred field of the card for the given property.
func (c Card) Preferred(k string) *Field {
	fields := c[k]
	if len(fields) == 0 {
		return nil
	}

	field := fields[0]
	max := 0
	for _, f := range fields {
		n := 0
		if pref := f.Params.Get(ParamPreferred); pref != "" {
			n, _ = strconv.Atoi(pref)
		} else if f.Params.HasType("pref") {
			// Apple Contacts adds "pref" to the TYPE param
			n = 1
		}

		if n > max {
			max = n
			field = f
		}
	}
	return field
}

// Value returns the first field value of the card for the given property. If
// there is no such field, it returns an empty string.
func (c Card) Value(k string) string {
	f := c.Get(k)
	if f == nil {
		return ""
	}
	return f.Value
}

// PreferredValue returns the preferred field value of the card.
func (c Card) PreferredValue(k string) string {
	f := c.Preferred(k)
	if f == nil {
		return ""
	}
	return f.Value
}

// Values returns a list of values for a given property.
func (c Card) Values(k string) []string {
	fields := c[k]
	if fields == nil {
		return nil
	}

	values := make([]string, len(fields))
	for i, f := range fields {
		values[i] = f.Value
	}
	return values
}

// Kind returns the kind of the object represented by this card. If it isn't
// specified, it returns the default: KindIndividual.
func (c Card) Kind() Kind {
	kind := strings.ToLower(c.Value(FieldKind))
	if kind == "" {
		return KindIndividual
	}
	return Kind(kind)
}

// FormattedNames returns formatted names of the card. The length of the result
// is always greater or equal to 1.
func (c Card) FormattedNames() []*Field {
	fns := c[FieldFormattedName]
	if len(fns) == 0 {
		return []*Field{{Value: ""}}
	}
	return fns
}

// Names returns names of the card.
func (c Card) Names() []*Name {
	ns := c[FieldName]
	if ns == nil {
		return nil
	}

	names := make([]*Name, len(ns))
	for i, n := range ns {
		names[i] = newName(n)
	}
	return names
}

// Name returns the preferred name of the card. If it isn't specified, it
// returns nil.
func (c Card) Name() *Name {
	n := c.Preferred(FieldName)
	if n == nil {
		return nil
	}
	return newName(n)
}

// Gender returns this card's gender.
func (c Card) Gender() (sex Sex, identity string) {
	v := c.Value(FieldKind)
	parts := strings.SplitN(v, ";", 2)
	return Sex(strings.ToLower(parts[0])), maybeGet(parts, 1)
}

// Addresses returns addresses of the card.
func (c Card) Addresses() []*Address {
	adrs := c[FieldAddress]
	if adrs == nil {
		return nil
	}

	addresses := make([]*Address, len(adrs))
	for i, adr := range adrs {
		addresses[i] = newAddress(adr)
	}
	return addresses
}

// Address returns the preferred address of the card. If it isn't specified, it
// returns nil.
func (c Card) Address() *Address {
	adr := c.Preferred(FieldAddress)
	if adr == nil {
		return nil
	}
	return newAddress(adr)
}

// Categories returns category information about the card, also known as "tags".
func (c Card) Categories() []string {
	return strings.Split(c.PreferredValue(FieldCategories), ",")
}

// Revision returns revision information about the current card.
func (c Card) Revision() (time.Time, error) {
	rev := c.Value(FieldRevision)
	if rev == "" {
		return time.Time{}, nil
	}
	return time.Parse(timestampLayout, rev)
}

// A field contains a value and some parameters.
type Field struct {
	Value string
	Params Params
	Group string
}

// Params is a set of field parameters.
type Params map[string][]string

// Get returns the first value with the key k. It returns an empty string if
// there is no such value.
func (p Params) Get(k string) string {
	values := p[k]
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

// Types returns the field types.
func (p Params) Types() []string {
	types := p[ParamType]
	list := make([]string, len(types))
	for i, t := range types {
		list[i] = strings.ToLower(t)
	}
	return list
}

// HasType returns true if and only if the field have the provided type.
func (p Params) HasType(t string) bool {
	for _, tt := range p[ParamType] {
		if strings.EqualFold(t, tt) {
			return true
		}
	}
	return false
}

// Kind is an object's kind.
type Kind string

// Values for FieldKind.
const (
	KindIndividual Kind = "individual"
	KindGroup = "group"
	KindOrg = "org"
	KindLocation = "location"
)

// Values for ParamType.
const (
	// Generic
	TypeHome = "home"
	TypeWork = "work"

	// For FieldTelephone
	TypeText = "text"
	TypeVoice = "voice" // Default
	TypeFax = "fax"
	TypeCell = "cell"
	TypeVideo = "video"
	TypePager = "pager"
	TypeTextPhone = "textphone"

	// For FieldRelated
	TypeContact = "contact"
	TypeAcquaintance = "acquaintance"
	TypeFriend = "friend"
	TypeMet = "met"
	TypeCoWorker = "co-worker"
	TypeColleague = "colleague"
	TypeCoResident = "co-resident"
	TypeNeighbor = "neighbor"
	TypeChild = "child"
	TypeParent = "parent"
	TypeSibling = "sibling"
	TypeSpouse = "spouse"
	TypeKin = "kin"
	TypeMuse = "muse"
	TypeCrush = "crush"
	TypeDate = "date"
	TypeSweetheart = "sweetheart"
	TypeMe = "me"
	TypeAgent = "agent"
	TypeEmergency = "emergency"
)

// Name contains an object's name components.
type Name struct {
	*Field

	FamilyName string
	GivenName string
	AdditionalName string
	HonorificPrefix string
	HonorificSuffix string
}

func newName(field *Field) *Name {
	components := strings.Split(field.Value, ";")
	return &Name{
		field,
		maybeGet(components, 0),
		maybeGet(components, 1),
		maybeGet(components, 2),
		maybeGet(components, 3),
		maybeGet(components, 4),
	}
}

// Sex is an object's biological sex.
type Sex string

const (
	SexUnspecified Sex = ""
	SexFemale = "F"
	SexMale = "M"
	SexOther = "O"
	SexNone = "N"
	SexUnknown = "U"
)

// An Address is a delivery address.
type Address struct {
	*Field

	PostOfficeBox string
	ExtendedAddress string // e.g., apartment or suite number
	StreetAddress string
	Locality string // e.g., city
	Region string // e.g., state or province
	PostalCode string
	Country string
}

func newAddress(field *Field) *Address {
	components := strings.Split(field.Value, ";")
	return &Address{
		field,
		maybeGet(components, 0),
		maybeGet(components, 1),
		maybeGet(components, 2),
		maybeGet(components, 3),
		maybeGet(components, 4),
		maybeGet(components, 5),
		maybeGet(components, 6),
	}
}
<commit_msg>Adds setters<commit_after>// Package vcard implements the vCard format, defined in RFC 6350.
package vcard

import (
	"strconv"
	"strings"
	"time"
)

const timestampLayout = "20060102T150405Z"

// Card property parameters.
const (
	ParamLanguage = "LANGUAGE"
	ParamValue = "VALUE"
	ParamPreferred = "PREF"
	ParamAltID = "ALTID"
	ParamPID = "PID"
	ParamType = "TYPE"
	ParamMediaType = "MEDIATYPE"
	ParamCalendarScale = "CALSCALE"
	ParamSortAs = "SORT-AS"
	ParamGeo = "GEO"
	ParamTimezone = "TZ"
)

// Card properties.
const (
	// General Properties
	FieldSource = "SOURCE"
	FieldKind = "KIND"
	FieldXML = "XML"

	// Identification Properties
	FieldFormattedName = "FN"
	FieldName = "N"
	FieldNickname = "NICKNAME"
	FieldPhoto = "PHOTO"
	FieldBirthday = "BDAY"
	FieldAnniversary = "ANIVERSARY"
	FieldGender = "GENDER"

	// Delivery Addressing Properties
	FieldAddress = "ADR"

	// Communications Properties
	FieldTelephone = "TEL"
	FieldEmail = "EMAIL"
	FieldIMPP = "IMPP" // Instant Messaging and Presence Protocol
	FieldLanguage = "LANG"

	// Geographical Properties
	FieldTimezone = "TZ"
	FieldGeo = "GEO"

	// Organizational Properties
	FieldTitle = "TITLE"
	FieldRole = "ROLE"
	FieldLogo = "LOGO"
	FieldOrganization = "ORG"
	FieldMember = "MEMBER"
	FieldRelated = "RELATED"

	// Explanatory Properties
	FieldCategories = "CATEGORIES"
	FieldNote = "NOTE"
	FieldProductID = "PRODID"
	FieldRevision = "REV"
	FieldSound = "SOUND"
	FieldUID = "UID"
	FieldClientPIDMap = "CLIENTPIDMAP"
	FieldURL = "URL"
	FieldVersion = "VERSION"

	// Security Properties
	FieldKey = "KEY"

	// Calendar Properties
	FieldFBURL = "FBURL"
	FieldCalendarAddressURI = "CALADRURI"
	FieldCalendarURI = "CALURI"
)

func maybeGet(l []string, i int) string {
	if i < len(l) {
		return l[i]
	}
	return ""
}

// A Card is an address book entry.
type Card map[string][]*Field

// Get returns the first field of the card for the given property. If there is
// no such field, it returns nil.
func (c Card) Get(k string) *Field {
	fields := c[k]
	if len(fields) == 0 {
		return nil
	}
	return fields[0]
}

// Add adds the k, f pair to the list of fields. It appends to any existing
// fields.
func (c Card) Add(k string, f *Field) {
	c[k] = append(c[k], f)
}

// Set sets the key k to the single field f. It replaces any existing field.
func (c Card) Set(k string, f *Field) {
	c[k] = []*Field{f}
}

// Preferred returns the preferred field of the card for the given property.
func (c Card) Preferred(k string) *Field {
	fields := c[k]
	if len(fields) == 0 {
		return nil
	}

	field := fields[0]
	max := 0
	for _, f := range fields {
		n := 0
		if pref := f.Params.Get(ParamPreferred); pref != "" {
			n, _ = strconv.Atoi(pref)
		} else if f.Params.HasType("pref") {
			// Apple Contacts adds "pref" to the TYPE param
			n = 1
		}

		if n > max {
			max = n
			field = f
		}
	}
	return field
}

// Value returns the first field value of the card for the given property. If
// there is no such field, it returns an empty string.
func (c Card) Value(k string) string {
	f := c.Get(k)
	if f == nil {
		return ""
	}
	return f.Value
}

// AddValue adds the k, v pair to the list of field values. It appends to any
// existing values.
func (c Card) AddValue(k, v string) {
	c.Add(k, &Field{Value: v})
}

// SetValue sets the field k to the single value v. It replaces any existing
// value.
func (c Card) SetValue(k, v string) {
	c.Set(k, &Field{Value: v})
}

// PreferredValue returns the preferred field value of the card.
func (c Card) PreferredValue(k string) string {
	f := c.Preferred(k)
	if f == nil {
		return ""
	}
	return f.Value
}

// Values returns a list of values for a given property.
func (c Card) Values(k string) []string {
	fields := c[k]
	if fields == nil {
		return nil
	}

	values := make([]string, len(fields))
	for i, f := range fields {
		values[i] = f.Value
	}
	return values
}

// Kind returns the kind of the object represented by this card. If it isn't
// specified, it returns the default: KindIndividual.
func (c Card) Kind() Kind {
	kind := strings.ToLower(c.Value(FieldKind))
	if kind == "" {
		return KindIndividual
	}
	return Kind(kind)
}

// SetKind sets the kind of the object represented by this card.
func (c Card) SetKind(kind Kind) {
	c.SetValue(FieldKind, string(kind))
}

// FormattedNames returns formatted names of the card. The length of the result
// is always greater or equal to 1.
func (c Card) FormattedNames() []*Field {
	fns := c[FieldFormattedName]
	if len(fns) == 0 {
		return []*Field{{Value: ""}}
	}
	return fns
}

// Names returns names of the card.
func (c Card) Names() []*Name {
	ns := c[FieldName]
	if ns == nil {
		return nil
	}

	names := make([]*Name, len(ns))
	for i, n := range ns {
		names[i] = newName(n)
	}
	return names
}

// Name returns the preferred name of the card. If it isn't specified, it
// returns nil.
func (c Card) Name() *Name {
	n := c.Preferred(FieldName)
	if n == nil {
		return nil
	}
	return newName(n)
}

// AddName adds the specified name to the list of names.
func (c Card) AddName(name *Name) {
	c.Add(FieldName, name.field())
}

// Gender returns this card's gender.
func (c Card) Gender() (sex Sex, identity string) {
	v := c.Value(FieldGender)
	parts := strings.SplitN(v, ";", 2)
	return Sex(strings.ToLower(parts[0])), maybeGet(parts, 1)
}

// SetGender sets this card's gender.
func (c Card) SetGender(sex Sex, identity string) {
	v := string(sex)
	if identity != "" {
		v += ";" + identity
	}
	c.SetValue(FieldGender, v)
}

// Addresses returns addresses of the card.
func (c Card) Addresses() []*Address {
	adrs := c[FieldAddress]
	if adrs == nil {
		return nil
	}

	addresses := make([]*Address, len(adrs))
	for i, adr := range adrs {
		addresses[i] = newAddress(adr)
	}
	return addresses
}

// Address returns the preferred address of the card. If it isn't specified, it
// returns nil.
func (c Card) Address() *Address {
	adr := c.Preferred(FieldAddress)
	if adr == nil {
		return nil
	}
	return newAddress(adr)
}

// AddAddress adds an address to the list of addresses.
func (c Card) AddAddress(address *Address) {
	c.Add(FieldAddress, address.field())
}

// Categories returns category information about the card, also known as "tags".
func (c Card) Categories() []string {
	return strings.Split(c.PreferredValue(FieldCategories), ",")
}

// SetCategories sets category information about the card.
func (c Card) SetCategories(categories []string) {
	c.SetValue(FieldCategories, strings.Join(categories, ","))
}

// Revision returns revision information about the current card.
func (c Card) Revision() (time.Time, error) {
	rev := c.Value(FieldRevision)
	if rev == "" {
		return time.Time{}, nil
	}
	return time.Parse(timestampLayout, rev)
}

// SetRevision sets revision information about the current card.
func (c Card) SetRevision(t time.Time) {
	c.SetValue(FieldRevision, t.Format(timestampLayout))
}

// A field contains a value and some parameters.
type Field struct {
	Value string
	Params Params
	Group string
}

// Params is a set of field parameters.
type Params map[string][]string

// Get returns the first value with the key k. It returns an empty string if
// there is no such value.
func (p Params) Get(k string) string {
	values := p[k]
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

// Add adds the k, v pair to the list of parameters. It appends to any existing
// values.
func (p Params) Add(k, v string) {
	p[k] = append(p[k], v)
}

// Set sets the parameter k to the single value v. It replaces any existing
// value.
func (p Params) Set(k, v string) {
	p[k] = []string{v}
}

// Types returns the field types.
func (p Params) Types() []string {
	types := p[ParamType]
	list := make([]string, len(types))
	for i, t := range types {
		list[i] = strings.ToLower(t)
	}
	return list
}

// HasType returns true if and only if the field have the provided type.
func (p Params) HasType(t string) bool {
	for _, tt := range p[ParamType] {
		if strings.EqualFold(t, tt) {
			return true
		}
	}
	return false
}

// Kind is an object's kind.
type Kind string

// Values for FieldKind.
const (
	KindIndividual Kind = "individual"
	KindGroup = "group"
	KindOrg = "org"
	KindLocation = "location"
)

// Values for ParamType.
const (
	// Generic
	TypeHome = "home"
	TypeWork = "work"

	// For FieldTelephone
	TypeText = "text"
	TypeVoice = "voice" // Default
	TypeFax = "fax"
	TypeCell = "cell"
	TypeVideo = "video"
	TypePager = "pager"
	TypeTextPhone = "textphone"

	// For FieldRelated
	TypeContact = "contact"
	TypeAcquaintance = "acquaintance"
	TypeFriend = "friend"
	TypeMet = "met"
	TypeCoWorker = "co-worker"
	TypeColleague = "colleague"
	TypeCoResident = "co-resident"
	TypeNeighbor = "neighbor"
	TypeChild = "child"
	TypeParent = "parent"
	TypeSibling = "sibling"
	TypeSpouse = "spouse"
	TypeKin = "kin"
	TypeMuse = "muse"
	TypeCrush = "crush"
	TypeDate = "date"
	TypeSweetheart = "sweetheart"
	TypeMe = "me"
	TypeAgent = "agent"
	TypeEmergency = "emergency"
)

// Name contains an object's name components.
type Name struct {
	*Field

	FamilyName string
	GivenName string
	AdditionalName string
	HonorificPrefix string
	HonorificSuffix string
}

func newName(field *Field) *Name {
	components := strings.Split(field.Value, ";")
	return &Name{
		field,
		maybeGet(components, 0),
		maybeGet(components, 1),
		maybeGet(components, 2),
		maybeGet(components, 3),
		maybeGet(components, 4),
	}
}

func (n *Name) field() *Field {
	n.Field.Value = strings.Join([]string{
		n.FamilyName,
		n.GivenName,
		n.AdditionalName,
		n.HonorificPrefix,
		n.HonorificSuffix,
	}, ";")
	return n.Field
}

// Sex is an object's biological sex.
type Sex string

const (
	SexUnspecified Sex = ""
	SexFemale = "F"
	SexMale = "M"
	SexOther = "O"
	SexNone = "N"
	SexUnknown = "U"
)

// An Address is a delivery address.
type Address struct {
	*Field

	PostOfficeBox string
	ExtendedAddress string // e.g., apartment or suite number
	StreetAddress string
	Locality string // e.g., city
	Region string // e.g., state or province
	PostalCode string
	Country string
}

func newAddress(field *Field) *Address {
	components := strings.Split(field.Value, ";")
	return &Address{
		field,
		maybeGet(components, 0),
		maybeGet(components, 1),
		maybeGet(components, 2),
		maybeGet(components, 3),
		maybeGet(components, 4),
		maybeGet(components, 5),
		maybeGet(components, 6),
	}
}

func (a *Address) field() *Field {
	a.Field.Value = strings.Join([]string{
		a.PostOfficeBox,
		a.ExtendedAddress,
		a.StreetAddress,
		a.Locality,
		a.Region,
		a.PostalCode,
		a.Country,
	}, ";")
	return a.Field
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"math"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 640
)

type ChatUI struct {
	Lines [chatHistoryLines]chat.AnyComponent

	container       *ui.Container
	parts           []*chatLine
	input           *ui.Text
	inputBackground *ui.Image

	enteringText    bool
	wasEnteringText bool
	inputLine       []rune
	cursorTick      float64
}

type chatLine struct {
	fade       float64
	text       *ui.Formatted
	background *ui.Image
}

func (c *ChatUI) init() {
	c.container = ui.NewContainer(0, 44, maxLineWidth, chatHistoryLines*18+2)
	c.container.Attach(ui.Bottom, ui.Left)
	c.input = ui.NewText("", 5, 1, 255, 255, 255).Attach(ui.Bottom, ui.Left)
	c.input.SetDraw(false)
	c.input.AttachTo(c.container)
	c.inputBackground = ui.NewImage(render.GetTexture("solid"), 0, 0, maxLineWidth, 20, 0, 0, 1, 1, 0, 0, 0).Attach(ui.Bottom, ui.Left)
	c.inputBackground.SetA(77)
	c.inputBackground.AttachTo(c.container)
	c.inputBackground.SetDraw(false)
	Client.scene.AddDrawable(c.inputBackground)
	Client.scene.AddDrawable(c.input)
}

func (c *ChatUI) Draw(delta float64) {
	if c.wasEnteringText != c.enteringText {
		if c.wasEnteringText {
			c.input.SetDraw(false)
			c.inputBackground.SetDraw(false)
			c.input.Update(string(c.inputLine))
			for _, p := range c.parts {
				p.text.SetY(p.text.Y() + 18)
				p.background.SetY(p.background.Y() + 18)
			}
		} else {
			for _, p := range c.parts {
				p.text.SetY(p.text.Y() - 18)
				p.background.SetY(p.background.Y() - 18)
			}
		}
		c.wasEnteringText = c.enteringText
	}
	if c.enteringText {
		c.input.SetDraw(true)
		c.inputBackground.SetDraw(true)
		c.cursorTick += delta
		// Add on our cursor
		if int(c.cursorTick/30)%2 == 0 {
			c.input.Update(string(c.inputLine) + "|")
		} else {
			c.input.Update(string(c.inputLine))
		}
		// Lazy way of preventing rounding errors buiding up over time
		if c.cursorTick > 0xFFFFFF {
			c.cursorTick = 0
		}
	}

	limit := 0.0
	if c.enteringText {
		limit = -18
	}
	for i := 0; i < len(c.parts); i++ {
		p := c.parts[i]
		if p.background.Y() < limit {
			c.parts = c.parts[i+1:]
			i--
			p.text.Remove()
			p.background.Remove()
		} else {
			p.fade -= 0.005 * delta
			if p.fade < 0 {
				p.fade = 0
			}
			for _, t := range p.text.Text {
				if c.enteringText {
					t.SetA(255)
				} else {
					t.SetA(int(255 * p.fade))
				}
			}
			ba := 0.3
			if !c.enteringText {
				ba -= (1.0 - p.fade) / 2.0
				ba = math.Min(ba, 0.3)
			}
			p.background.SetA(int(255 * ba))
			if p.background.A() < 0 {
				p.background.SetA(0)
			}
		}
	}
}

func (c *ChatUI) handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if (key == glfw.KeyEscape || key == glfw.KeyEnter) && action == glfw.Release {
		if key == glfw.KeyEnter && len(c.inputLine) != 0 {
			Client.network.Write(&protocol.ChatMessage{string(c.inputLine)})
		}
		// Return control back to the default
		c.enteringText = false
		c.inputLine = c.inputLine[:0]
		lockMouse = true
		w.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
		w.SetCharCallback(nil)
		return
	}
	if key == glfw.KeyBackspace && action != glfw.Release {
		if len(c.inputLine) > 0 {
			c.inputLine = c.inputLine[:len(c.inputLine)-1]
		}
	}
}

func (c *ChatUI) handleChar(w *glfw.Window, char rune) {
	if len(c.inputLine) < 100 {
		c.inputLine = append(c.inputLine, char)
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	chat.ConvertLegacy(msg)
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	c.Lines[chatHistoryLines-1] = msg
	f := ui.NewFormattedWidth(msg, 5, chatHistoryLines*18+1, maxLineWidth-10).Attach(ui.Top, ui.Left)
	f.AttachTo(c.container)
	line := &chatLine{
		text:       f,
		fade:       3.0,
		background: ui.NewImage(render.GetTexture("solid"), 0, chatHistoryLines*18, maxLineWidth, f.Height, 0, 0, 1, 1, 0, 0, 0),
	}
	line.background.AttachTo(c.container)
	line.background.SetA(77)
	c.parts = append(c.parts, line)
	Client.scene.AddDrawable(line.background)
	Client.scene.AddDrawable(f)
	ff := f
	for _, f := range c.parts {
		f.text.SetY(f.text.Y() - 18*float64(ff.Lines))
		f.background.SetY(f.background.Y() - 18*float64(ff.Lines))
	}
	if c.enteringText {
		ff.SetY(ff.Y() - 18)
		line.background.SetY(line.background.Y() - 18)
	}
}
<commit_msg>steven: correct handling of chat slices<commit_after>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"math"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 640
)

type ChatUI struct {
	Lines [chatHistoryLines]chat.AnyComponent

	container       *ui.Container
	parts           []*chatLine
	input           *ui.Text
	inputBackground *ui.Image

	enteringText    bool
	wasEnteringText bool
	inputLine       []rune
	cursorTick      float64
}

type chatLine struct {
	fade       float64
	text       *ui.Formatted
	background *ui.Image
}

func (c *ChatUI) init() {
	c.container = ui.NewContainer(0, 44, maxLineWidth, chatHistoryLines*18+2)
	c.container.Attach(ui.Bottom, ui.Left)
	c.input = ui.NewText("", 5, 1, 255, 255, 255).Attach(ui.Bottom, ui.Left)
	c.input.SetDraw(false)
	c.input.AttachTo(c.container)
	c.inputBackground = ui.NewImage(render.GetTexture("solid"), 0, 0, maxLineWidth, 20, 0, 0, 1, 1, 0, 0, 0).Attach(ui.Bottom, ui.Left)
	c.inputBackground.SetA(77)
	c.inputBackground.AttachTo(c.container)
	c.inputBackground.SetDraw(false)
	Client.scene.AddDrawable(c.inputBackground)
	Client.scene.AddDrawable(c.input)
}

func (c *ChatUI) Draw(delta float64) {
	if c.wasEnteringText != c.enteringText {
		if c.wasEnteringText {
			c.input.SetDraw(false)
			c.inputBackground.SetDraw(false)
			c.input.Update(string(c.inputLine))
			for _, p := range c.parts {
				p.text.SetY(p.text.Y() + 18)
				p.background.SetY(p.background.Y() + 18)
			}
		} else {
			for _, p := range c.parts {
				p.text.SetY(p.text.Y() - 18)
				p.background.SetY(p.background.Y() - 18)
			}
		}
		c.wasEnteringText = c.enteringText
	}
	if c.enteringText {
		c.input.SetDraw(true)
		c.inputBackground.SetDraw(true)
		c.cursorTick += delta
		// Add on our cursor
		if int(c.cursorTick/30)%2 == 0 {
			c.input.Update(string(c.inputLine) + "|")
		} else {
			c.input.Update(string(c.inputLine))
		}
		// Lazy way of preventing rounding errors buiding up over time
		if c.cursorTick > 0xFFFFFF {
			c.cursorTick = 0
		}
	}

	limit := 0.0
	if c.enteringText {
		limit = -18
	}
	for i := 0; i < len(c.parts); i++ {
		p := c.parts[i]
		if p.background.Y() < limit {
			c.parts = append(c.parts[:i], c.parts[i+1:]...)
			i--
			p.text.Remove()
			p.background.Remove()
		} else {
			p.fade -= 0.005 * delta
			if p.fade < 0 {
				p.fade = 0
			}
			for _, t := range p.text.Text {
				if c.enteringText {
					t.SetA(255)
				} else {
					t.SetA(int(255 * p.fade))
				}
			}
			ba := 0.3
			if !c.enteringText {
				ba -= (1.0 - p.fade) / 2.0
				ba = math.Min(ba, 0.3)
			}
			p.background.SetA(int(255 * ba))
			if p.background.A() < 0 {
				p.background.SetA(0)
			}
		}
	}
}

func (c *ChatUI) handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if (key == glfw.KeyEscape || key == glfw.KeyEnter) && action == glfw.Release {
		if key == glfw.KeyEnter && len(c.inputLine) != 0 {
			Client.network.Write(&protocol.ChatMessage{string(c.inputLine)})
		}
		// Return control back to the default
		c.enteringText = false
		c.inputLine = c.inputLine[:0]
		lockMouse = true
		w.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
		w.SetCharCallback(nil)
		return
	}
	if key == glfw.KeyBackspace && action != glfw.Release {
		if len(c.inputLine) > 0 {
			c.inputLine = c.inputLine[:len(c.inputLine)-1]
		}
	}
}

func (c *ChatUI) handleChar(w *glfw.Window, char rune) {
	if len(c.inputLine) < 100 {
		c.inputLine = append(c.inputLine, char)
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	chat.ConvertLegacy(msg)
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	c.Lines[chatHistoryLines-1] = msg
	f := ui.NewFormattedWidth(msg, 5, chatHistoryLines*18+1, maxLineWidth-10).Attach(ui.Top, ui.Left)
	f.AttachTo(c.container)
	line := &chatLine{
		text:       f,
		fade:       3.0,
		background: ui.NewImage(render.GetTexture("solid"), 0, chatHistoryLines*18, maxLineWidth, f.Height, 0, 0, 1, 1, 0, 0, 0),
	}
	line.background.AttachTo(c.container)
	line.background.SetA(77)
	c.parts = append(c.parts, line)
	Client.scene.AddDrawable(line.background)
	Client.scene.AddDrawable(f)
	ff := f
	for _, f := range c.parts {
		f.text.SetY(f.text.Y() - 18*float64(ff.Lines))
		f.background.SetY(f.background.Y() - 18*float64(ff.Lines))
	}
	if c.enteringText {
		ff.SetY(ff.Y() - 18)
		line.background.SetY(line.background.Y() - 18)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/gob"
	"fmt"
	"github.com/apcera/nats"
	"golang.org/x/crypto/nacl/box"
	"golang.org/x/crypto/ssh/terminal"
	"log"
	"os"
	"runtime"
	"strings"
)

func MarshalPublicKey(c *[32]byte) string {
	return fmt.Sprintf("%x", *c)
}

func UnmarshalPublicKey(s string) *[32]byte {
	if len(s) != 64 {
		panic("Malformed public key (wrong length)")
	}
	result := new([32]byte)
	fmt.Sscanf(s, "%x", result)
	return result
}

type Room struct {
	Room string
}

type Party struct {
	Nick string
	Key  string
}

// messages from clients to secretary
type JoinRequest struct {
	Party
}

type LeaveRequest struct {
	Party
}

// messages from secretary to clients
type Message struct {
	Party
	Message string
}

type Members struct {
	Parties []Party
}

func Init() {
	gob.Register(JoinRequest{})
	gob.Register(LeaveRequest{})
	gob.Register(Message{})
	gob.Register(Members{})
}

type RoomState struct {
	Sub     *nats.Subscription
	Members []Party
}

func encode(p interface{}) []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&p)
	if err != nil {
		log.Fatal("encode:", err)
	}
	return buf.Bytes()
}

var MyPrivateKey *[32]byte
var MyPublicKey string
var MyParty Party
var MyRooms map[string]*RoomState
var MyRoom string
var MyNick string

func initialize() {
	Init()
	rawPrivateKey, rawPublicKey, _ := box.GenerateKey(rand.Reader)
	MyPrivateKey = rawPrivateKey
	MyPublicKey = MarshalPublicKey(rawPublicKey)
	MyNick = "AnonymousCoward"
	MyParty = Party{MyNick, MyPublicKey}
	MyRooms = make(map[string]*RoomState)
}

func changeNick(nick string) {
	MyNick = nick
	MyParty = Party{MyNick, MyPublicKey}
}

func escape(s string) []byte {
	in := []byte(s)
	var out []byte
	for _, b := range in {
		switch {
		case b == '\t':
			out = append(out, ' ')
		case b == '\r':
			continue
		case b == '\n':
			out = append(out, '\n')
		case b < 32:
			out = append(out, '?')
		default:
			out = append(out, b)
		}
	}
	return out
}

func Tprintf(term *terminal.Terminal, format string, v ...interface{}) {
	term.Write(escape(fmt.Sprintf(format, v...)))
}

func client() {
	initialize()
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err.Error())
	}
	defer terminal.Restore(0, oldState)

	term := terminal.NewTerminal(os.Stdin, "> ")
	Tprintf(term, "Greetings, %s!\n", MyNick)
	Tprintf(term, "Commands:\n")
	Tprintf(term, "join foo      (join the chatroom named foo)\n")
	Tprintf(term, "leave foo     (leave the chatroom named foo)\n")
	Tprintf(term, "nick foo      (change your nickname to foo)\n")
	Tprintf(term, "members       (list the parties in the current room)\n")
	Tprintf(term, "^D            (buh-bye)\n")
	Tprintf(term, "anything else (send anything else to your current chatroom)\n")

	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		panic("unable to connect to NATS server")
	}

	for {
		line, err := term.ReadLine()
		if err != nil {
			break
		}
		words := strings.Fields(line)
		if len(words) == 0 {
			continue
		}
		switch words[0] {
		case "nick":
			if len(words) == 2 {
				changeNick(words[1])
				Tprintf(term, "Your nickname is now %s\n", MyNick)

			} else {
				Tprintf(term, "Nicknames must be one word\n")
			}
		case "join":
			if len(words) == 2 {
				room := words[1]
				MyRoom = room
				term.SetPrompt(fmt.Sprintf("[%s]> ", MyRoom))
				_, ok := MyRooms[room]
				if !ok {
					joinTerm(nc, term, room)
				}
			} else {
				Tprintf(term, "Join what room?\n")
			}
		case "rooms":
			for room, _ := range MyRooms {
				Tprintf(term, "%s\n", room)
			}
		case "leave":
			words = words[1:]
			for _, room := range words {
				if st, ok := MyRooms[room]; ok {
					delete(MyRooms, room)
					err = st.Sub.Unsubscribe()
					checkError(err)
					if MyRoom == room {
						if len(MyRooms) == 0 {
							MyRoom = ""
							term.SetPrompt(fmt.Sprintf("> "))
						} else {
							for r, _ := range MyRooms {
								MyRoom = r
								term.SetPrompt(fmt.Sprintf("[%s]> ", MyRoom))
								break
							}
						}
					}
				} else {
					Tprintf(term, "Not a member of %s\n", room)
				}
			}
		case "members":
			if MyRoom == "" {
				Tprintf(term, "You must join a room before you can see the members of the room\n")
			} else {
				st := MyRooms[MyRoom]
				for _, member := range st.Members {
					Tprintf(term, "%s (%s)\n", member.Key, member.Nick)
				}
			}
		default:
			if MyRoom != "" {
				msg := strings.TrimSpace(line)
				err = nc.Publish(MyRoom, encode(Message{MyParty, msg}))
				checkError(err)
			} else {
				Tprintf(term, "You must join a room first\n")
			}
		}
	}
}

func joinTerm(nc *nats.Conn, term *terminal.Terminal, rm string) {
	err := nc.Publish(fmt.Sprintf("secretary.%s", rm), encode(JoinRequest{MyParty}))
	checkError(err)
	sub, err := nc.Subscribe(rm, func(m *nats.Msg) {
		dec := gob.NewDecoder(bytes.NewBuffer(m.Data))
		var p interface{}
		err := dec.Decode(&p)
		if err != nil {
			log.Fatal("decode:", err)
		}
		switch r := p.(type) {
		case Message:
			if r.Party != MyParty {
				Tprintf(term, "[%s]: %s -- (%s)\n", rm, r.Message, r.Party.Nick)
			}
		case Members:
			MyRooms[rm].Members = r.Parties
		}
	})
	checkError(err)
	MyRooms[rm] = &RoomState{sub, nil} // needs lock
}

func secretary() {
	log.Println("starting secretary")
	initialize()
	changeNick("ChatAdministrator")
	rooms := make(map[string]bool)
	members := make(map[string](map[Party]bool))
	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		panic("unable to connect to NATS server")
	}
	nc.Subscribe("secretary.>", func(m *nats.Msg) {
		dec := gob.NewDecoder(bytes.NewBuffer(m.Data))
		var p interface{}
		err := dec.Decode(&p)
		if err != nil {
			log.Fatal("decode:", err)
		}
		room := m.Subject[len("secretary."):]
		if !rooms[room] {
			rooms[room] = true
		}
		switch r := p.(type) {
		case LeaveRequest:
			delete(members[room], r.Party)
			_ = nc.Publish(room, encode(Message{MyParty, fmt.Sprintf("%s has left the room", r.Party.Nick)}))
			log.Println("Leave", room, r)
		case JoinRequest:
			if _, ok := members[room]; !ok {
				members[room] = make(map[Party]bool)
			}
			members[room][r.Party] = true
			_ = nc.Publish(room, encode(Message{MyParty, fmt.Sprintf("%s has joined %s", r.Party.Nick, room)}))
			log.Println("Join", room, r)
			numMembers := len(members[room])
			parties := make([]Party, 0, numMembers)
			for party, _ := range members[room] {
				parties = append(parties, party)
			}
			_ = nc.Publish(room, encode(Members{parties}))
			log.Println("Members", room, members[room])
		case Message:
			log.Println("Message", r.Message)
		}
	})
	runtime.Goexit()
}

func checkError(err error) {
	if err != nil {
		log.Fatal("Error:", err)
	}
}

func main() {
	if len(os.Args) > 1 && os.Args[1] == "secretary" {
		secretary()
	} else {
		client()
	}
}
<commit_msg>chat: check for malformed public keys<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/sha1"
	"encoding/gob"
	"fmt"
	"github.com/apcera/nats"
	"golang.org/x/crypto/nacl/box"
	"golang.org/x/crypto/ssh/terminal"
	"io"
	"log"
	"os"
	"runtime"
	"strings"
)

func MarshalPublicKey(c *[32]byte) string {
	return fmt.Sprintf("%x", *c)
}

func UnmarshalPublicKey(s string) *[32]byte {
	if len(s) != 64 {
		panic("Malformed public key (wrong length)")
	}
	for _, v := range s {
		switch v {
		default:
			panic("Malformed public key (not lowercase hexidecimal)")
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f':
		}
	}
	result := new([32]byte)
	fmt.Sscanf(s, "%x", result)
	return result
}

type Room struct {
	Room string
}

type Party struct {
	Nick string
	Key  string
}

// messages from clients to secretary
type JoinRequest struct {
	Party
}

type LeaveRequest struct {
	Party
}

// messages from secretary to clients
type Message struct {
	Party
	Message string
}

type Members struct {
	Parties []Party
}

func Init() {
	gob.Register(JoinRequest{})
	gob.Register(LeaveRequest{})
	gob.Register(Message{})
	gob.Register(Members{})
}

type RoomState struct {
	Sub     *nats.Subscription
	Members []Party
}

func encode(p interface{}) []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&p)
	if err != nil {
		log.Fatal("encode:", err)
	}
	return buf.Bytes()
}

var MyPrivateKey *[32]byte
var MyPublicKey string
var MyParty Party
var MyRooms map[string]*RoomState
var MyRoom string
var MyNick string

func initialize() {
	Init()
	rawPrivateKey, rawPublicKey, _ := box.GenerateKey(rand.Reader)
	MyPrivateKey = rawPrivateKey
	MyPublicKey = MarshalPublicKey(rawPublicKey)
	MyNick = "AnonymousCoward"
	MyParty = Party{MyNick, MyPublicKey}
	MyRooms = make(map[string]*RoomState)
}

func changeNick(nick string) {
	MyNick = nick
	MyParty = Party{MyNick, MyPublicKey}
}

func escape(s string) []byte {
	in := []byte(s)
	var out []byte
	for _, b := range in {
		switch {
		case b == '\t':
			out = append(out, ' ')
		case b == '\r':
			continue
		case b == '\n':
			out = append(out, '\n')
		case b < 32:
			out = append(out, '?')
		default:
			out = append(out, b)
		}
	}
	return out
}

func Tprintf(term *terminal.Terminal, format string, v ...interface{}) {
	term.Write(escape(fmt.Sprintf(format, v...)))
}

func client() {
	initialize()
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err.Error())
	}
	defer terminal.Restore(0, oldState)

	term := terminal.NewTerminal(os.Stdin, "> ")
	Tprintf(term, "Greetings, %s!\n", MyNick)
	Tprintf(term, "Commands:\n")
	Tprintf(term, "join foo      (join the chatroom named foo)\n")
	Tprintf(term, "leave foo     (leave the chatroom named foo)\n")
	Tprintf(term, "nick foo      (change your nickname to foo)\n")
	Tprintf(term, "members       (list the parties in the current room)\n")
	Tprintf(term, "^D            (buh-bye)\n")
	Tprintf(term, "anything else (send anything else to your current chatroom)\n")

	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		panic("unable to connect to NATS server")
	}

	for {
		line, err := term.ReadLine()
		if err != nil {
			break
		}
		words := strings.Fields(line)
		if len(words) == 0 {
			continue
		}
		switch words[0] {
		case "nick":
			if len(words) == 2 {
				changeNick(words[1])
				Tprintf(term, "Your nickname is now %s\n", MyNick)

			} else {
				Tprintf(term, "Nicknames must be one word\n")
			}
		case "join":
			if len(words) == 2 {
				room := words[1]
				MyRoom = room
				term.SetPrompt(fmt.Sprintf("[%s]> ", MyRoom))
				_, ok := MyRooms[room]
				if !ok {
					joinTerm(nc, term, room)
				}
			} else {
				Tprintf(term, "Join what room?\n")
			}
		case "rooms":
			for room, _ := range MyRooms {
				Tprintf(term, "%s\n", room)
			}
		case "leave":
			words = words[1:]
			for _, room := range words {
				if st, ok := MyRooms[room]; ok {
					delete(MyRooms, room)
					err = st.Sub.Unsubscribe()
					checkError(err)
					if MyRoom == room {
						if len(MyRooms) == 0 {
							MyRoom = ""
							term.SetPrompt(fmt.Sprintf("> "))
						} else {
							for r, _ := range MyRooms {
								MyRoom = r
								term.SetPrompt(fmt.Sprintf("[%s]> ", MyRoom))
								break
							}
						}
					}
				} else {
					Tprintf(term, "Not a member of %s\n", room)
				}
			}
		case "members":
			if MyRoom == "" {
				Tprintf(term, "You must join a room before you can see the members of the room\n")
			} else {
				h := sha1.New()
				st := MyRooms[MyRoom]
				for _, member := range st.Members {
					io.WriteString(h, member.Key)
					Tprintf(term, "%s (%s)\n", member.Key, member.Nick)
				}
				Tprintf(term, "Hash: %x\n", h.Sum(nil))
			}
		default:
			if MyRoom != "" {
				msg := strings.TrimSpace(line)
				err = nc.Publish(MyRoom, encode(Message{MyParty, msg}))
				checkError(err)
			} else {
				Tprintf(term, "You must join a room first\n")
			}
		}
	}
}

func joinTerm(nc *nats.Conn, term *terminal.Terminal, rm string) {
	err := nc.Publish(fmt.Sprintf("secretary.%s", rm), encode(JoinRequest{MyParty}))
	checkError(err)
	sub, err := nc.Subscribe(rm, func(m *nats.Msg) {
		dec := gob.NewDecoder(bytes.NewBuffer(m.Data))
		var p interface{}
		err := dec.Decode(&p)
		if err != nil {
			log.Fatal("decode:", err)
		}
		switch r := p.(type) {
		case Message:
			if r.Party != MyParty {
				Tprintf(term, "[%s]: %s -- (%s)\n", rm, r.Message, r.Party.Nick)
			}
		case Members:
			MyRooms[rm].Members = r.Parties
		}
	})
	checkError(err)
	MyRooms[rm] = &RoomState{sub, nil} // needs lock
}

func secretary() {
	log.Println("starting secretary")
	initialize()
	changeNick("ChatAdministrator")
	rooms := make(map[string]bool)
	members := make(map[string](map[Party]bool))
	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		panic("unable to connect to NATS server")
	}
	nc.Subscribe("secretary.>", func(m *nats.Msg) {
		dec := gob.NewDecoder(bytes.NewBuffer(m.Data))
		var p interface{}
		err := dec.Decode(&p)
		if err != nil {
			log.Fatal("decode:", err)
		}
		room := m.Subject[len("secretary."):]
		if !rooms[room] {
			rooms[room] = true
		}
		switch r := p.(type) {
		case LeaveRequest:
			delete(members[room], r.Party)
			_ = nc.Publish(room, encode(Message{MyParty, fmt.Sprintf("%s has left the room", r.Party.Nick)}))
			log.Println("Leave", room, r)
		case JoinRequest:
			if _, ok := members[room]; !ok {
				members[room] = make(map[Party]bool)
			}
			members[room][r.Party] = true
			_ = nc.Publish(room, encode(Message{MyParty, fmt.Sprintf("%s has joined %s", r.Party.Nick, room)}))
			log.Println("Join", room, r)
			numMembers := len(members[room])
			parties := make([]Party, 0, numMembers)
			for party, _ := range members[room] {
				parties = append(parties, party)
			}
			_ = nc.Publish(room, encode(Members{parties}))
			log.Println("Members", room, members[room])
		case Message:
			log.Println("Message", r.Message)
		}
	})
	runtime.Goexit()
}

func checkError(err error) {
	if err != nil {
		log.Fatal("Error:", err)
	}
}

func main() {
	if len(os.Args) > 1 && os.Args[1] == "secretary" {
		secretary()
	} else {
		client()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"

	"github.com/num5/loger"
)

var (
	log *loger.Log
	confile = "config.yml"
)

const (
	HELP = `

Usage:

chca command [args...]

	初始化博客文件夹
    	chca init

	新建 markdown 文件
    	chca new filename

	编译博客
    	chca compile/c

	打开文件服务器
    	chca http/web port

	`
)

func PrintUsage() {
	fmt.Println(HELP)
}

var (
	args []string
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic 错误: %s\n", err)
		}
	}()

	flag.Parse()
	args = flag.Args()
	if len(args) == 0 || len(args) > 3 {
		PrintUsage()
		os.Exit(1)
	}

	switch args[0] {

	default:
		PrintUsage()
		os.Exit(1)
	case "init":
		new()
	case "new":
		if len(args) == 2 {
			name := args[1]

			CrearteMark(name)
		} else {
			log.Error("缺少文件名")
		}
	case "compile", "c":
		Compile()
	case "watch", "w":
		NewWatch(Config().Paths, Config().Exts).Watcher()
		done := make(chan bool)
		<-done
	case "run":
		Compile()
		NewWatch(Config().Paths, Config().Exts).Watcher()
		var port int = 9900
		if len(args) == 2 {
			p, err := strconv.Atoi(args[1])
			if err != nil {
				panic(err)
			}

			port = p
		}
		_http(port)

	case "http", "web":
		var port int = 9900
		if len(args) == 2 {
			p, err := strconv.Atoi(args[1])
			if err != nil {
				panic(err)
			}

			port = p
		}

		_http(port)
	}
}

func new() {
	createConf()
	createDir()
}

func _http(port int) {

	log.Trac("打开内置web服务器...")

	p := strconv.Itoa(port)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir(Config().Html+"/assets/"))))

	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(Config().Html))))

	f := newFileHandler(Config().UploadTheme, Config().Markdown)
	f.Http()

	log.Debugf("打开内置web服务器成功，监听端口 :%d...", port)

	err := http.ListenAndServe(":"+p, nil)

	if err != nil {
		log.Errorf("ListenAndServe: %s", err)
	}
}

func init() {

	// 初始化Log
	log = loger.NewLog(1000)
	// 设置log级别
	log.SetLevel("Debug")
	// 设置输出引擎
	log.SetEngine("file", `{"level":5, "spilt":"size", "filename":".logs/wechat.log", "maxsize":15}`)
	//log.DelEngine("console")
	// 设置是否输出行号
	//log.SetFuncCall(true)
}
<commit_msg> fix usage<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"

	"github.com/num5/loger"
)

var (
	log *loger.Log
	confile = "config.yml"
)

const (
	HELP = `

Usage:

chca command [args...]

	初始化博客文件夹
    	chca init

	新建 markdown 文件
    	chca new filename

	编译博客
    	chca compile/c

    打开文件监听器
    	chca watch/w

	打开文件服务器
    	chca http/web [port]

    运行chca所有服务，包括内置服务器、监听器
    	chca run [port]

	`
)

func PrintUsage() {
	fmt.Println(HELP)
}

var (
	args []string
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("panic 错误: %s\n", err)
		}
	}()

	flag.Parse()
	args = flag.Args()
	if len(args) == 0 || len(args) > 3 {
		PrintUsage()
		os.Exit(1)
	}

	switch args[0] {

	default:
		PrintUsage()
		os.Exit(1)
	case "init":
		new()
	case "new":
		if len(args) == 2 {
			name := args[1]

			CrearteMark(name)
		} else {
			log.Error("缺少文件名")
		}
	case "compile", "c":
		Compile()
	case "watch", "w":
		NewWatch(Config().Paths, Config().Exts).Watcher()
		done := make(chan bool)
		<-done
	case "run":
		Compile()
		NewWatch(Config().Paths, Config().Exts).Watcher()
		var port int = 9900
		if len(args) == 2 {
			p, err := strconv.Atoi(args[1])
			if err != nil {
				panic(err)
			}

			port = p
		}
		_http(port)

	case "http", "web":
		var port int = 9900
		if len(args) == 2 {
			p, err := strconv.Atoi(args[1])
			if err != nil {
				panic(err)
			}

			port = p
		}

		_http(port)
	}
}

func new() {
	createConf()
	createDir()
}

func _http(port int) {

	log.Trac("打开内置web服务器...")

	p := strconv.Itoa(port)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir(Config().Html+"/assets/"))))

	http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(Config().Html))))

	f := newFileHandler(Config().UploadTheme, Config().Markdown)
	f.Http()

	log.Debugf("打开内置web服务器成功，监听端口 :%d...", port)

	err := http.ListenAndServe(":"+p, nil)

	if err != nil {
		log.Errorf("ListenAndServe: %s", err)
	}
}

func init() {

	// 初始化Log
	log = loger.NewLog(1000)
	// 设置log级别
	log.SetLevel("Debug")
	// 设置输出引擎
	log.SetEngine("file", `{"level":5, "spilt":"size", "filename":".logs/wechat.log", "maxsize":15}`)
	//log.DelEngine("console")
	// 设置是否输出行号
	//log.SetFuncCall(true)
}
<|endoftext|>


<commit_before>package cmap

import "sync"

// CMap is a thread-safe map.
type CMap struct {
	items map[interface{}]interface{}
	mutex sync.RWMutex
}

// New creates and returns a new thread-safe map.
func New() *CMap {
	return &CMap{items: make(map[interface{}]interface{})}
}

// Get retrieves a value for a given key.
func (c *CMap) Get(key interface{}) (val interface{}, ok bool) {
	c.mutex.RLock()
	val, ok = c.items[key]
	c.mutex.RUnlock()
	return
}

// Len returns the item count.
func (c *CMap) Len() int {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	return len(c.items)
}

// Set stores a value for a given key.
func (c *CMap) Set(key interface{}, val interface{}) {
	c.mutex.Lock()
	c.items[key] = val
	c.mutex.Unlock()
}

// Delete removes a value for a given key.
func (c *CMap) Delete(key interface{}) {
	c.mutex.Lock()
	delete(c.items, key)
	c.mutex.Unlock()
}
<commit_msg>add range function<commit_after>package cmap

import "sync"

// CMap is a thread-safe map.
type CMap struct {
	items map[interface{}]interface{}
	mutex sync.RWMutex
}

// New creates and returns a new thread-safe map.
func New() *CMap {
	return &CMap{items: make(map[interface{}]interface{})}
}

// Get retrieves a value for a given key.
func (c *CMap) Get(key interface{}) (val interface{}, ok bool) {
	c.mutex.RLock()
	val, ok = c.items[key]
	c.mutex.RUnlock()
	return
}

// Range iterates over the entire collection and executes given function on each item.
func (c *CMap) Range(fn func(val interface{})) {
	c.mutex.RLock()
	for _, item := range c.items {
		fn(item)
	}
	c.mutex.RUnlock()
}

// Len returns the item count.
func (c *CMap) Len() int {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	return len(c.items)
}

// Set stores a value for a given key.
func (c *CMap) Set(key interface{}, val interface{}) {
	c.mutex.Lock()
	c.items[key] = val
	c.mutex.Unlock()
}

// Delete removes a value for a given key.
func (c *CMap) Delete(key interface{}) {
	c.mutex.Lock()
	delete(c.items, key)
	c.mutex.Unlock()
}
<|endoftext|>


<commit_before>package cmdr

import (
	"code.google.com/p/go.crypto/ssh"
	"io"
	"io/ioutil"
	"bufio"
	"os/exec"
	"fmt"
	"reflect"
	"errors"
	"strings"
	"time"
)

func NewClientConfig(username, password, pemfile string) (*ssh.ClientConfig, error) {
	if username != "" && password != "" {
		answers := keyboardInteractive(map[string]string{"Password: ": password,})
		return &ssh.ClientConfig{
			User: username,
			Auth: []ssh.AuthMethod{
				ssh.KeyboardInteractive(answers.Challenge),
			},
		}, nil
	}

	if username != "" && pemfile != "" {
		signer, err := loadPEM(pemfile)
		if err != nil {
			return nil, err
		}

		return &ssh.ClientConfig{
			User: username,
			Auth: []ssh.AuthMethod{
				ssh.PublicKeys(signer),
			},
		}, nil
	}

	return nil, fmt.Errorf("Missing valid arguments, must pass a (username and password) or (username and pemfile).")
}

type Command struct {
	Session *exec.Cmd
	Stdin	chan string
	Stdout	chan string
	Stderr	chan string
	ErrDelimiter byte
	OutDelimiter byte
	ErrMaxBytes int
	OutMaxBytes int
	timeout		time.Duration
}

type SSHCommand struct {
	Command
	Config *ssh.ClientConfig
	Server string
	Session *ssh.Session
	client *ssh.Client
	timeout		time.Duration
}

func NewCommand(inchan, outchan, errchan chan string) (*Command, error) {
	return &Command{Stdin: inchan, Stdout: outchan, Stderr: errchan, timeout: 0}, nil
}

func NewSSHCommand(cfg *ssh.ClientConfig, server string, inchan, outchan, errchan chan string) (*SSHCommand, error) {
	return &SSHCommand{Config: cfg, Server: server, Command: Command{Stdin: inchan, Stdout: outchan, Stderr: errchan}, timeout: 0}, nil
}

func timeout(cmd *exec.Cmd, timeout time.Duration, done chan bool, err chan error) {
	if timeout > 0 {
		response := ""
		for {
			select {
			case <-time.After(timeout):
				cmd.Process.Kill()
				response = "Local command execution timeout reached, terminating command"
			case <-done:
				if len(response) > 1 {
					err <-fmt.Errorf(response)
				} else {
					close(err)
				}
				return
			}
		}
	} else {
		err <-nil
	}
}

func timeout_ssh(client *ssh.Client, timeout time.Duration, done chan bool, err chan error) {
	if timeout > 0 {
		response := ""
		for {
			select {
			case <-time.After(timeout):
				client.Conn.Close()
				response = "Remote SSH command execution timeout reached, terminating command"
			case <-done:
				if len(response) > 1 {
					err <-fmt.Errorf(response)
				} else {
					close(err)
				}
				return
			}
		}
	} else {
		err <-nil
	}
}

func (c *Command) Execute(cmd string, args ...string) error {
	c.Session = exec.Command(cmd, args...)
	done := make(chan bool, 1)
	errchan := make(chan error, 1)
	defer close(done)
	go timeout(c.Session, c.timeout, done, errchan)

	if err := execute(c, ""); err != nil {
		fmt.Printf("Execute Error: %s\n", err)
		done <-true
		<-errchan
		return err
	}

	done <-true
	err := <-errchan
	return err
}

func (s *SSHCommand) Execute(cmd string) (err error) {
	s.client, err = ssh.Dial("tcp", s.Server, s.Config)
	if err != nil {
		return err
	}
	s.Session, err = s.client.NewSession()
	if err != nil {
		return err
	}

	done := make(chan bool, 1)
	errchan := make(chan error, 1)
	defer close(done)
	go timeout_ssh(s.client, s.timeout, done, errchan)

	if err = execute(s, cmd); err != nil {
		done <-true
		<-errchan
		return err
	}

	done <-true
	err = <-errchan
	return err
}

func (c *Command) SetTimeout(timeout time.Duration) {
	c.timeout = timeout
}

func (s *SSHCommand) SetTimeout(timeout time.Duration) {
	s.timeout = timeout
}

func (c *Command) ProcessStdIn(notifier chan error, w io.WriteCloser) {
	processInput(c.Stdin, notifier, w)
}

func (c *Command) ProcessStdOut(notifier chan error, r io.Reader)  {
	processOutput(c.Stdout, notifier, r, c.OutMaxBytes, c.OutDelimiter)
}

func (c *Command) ProcessStdErr(notifier chan error, r io.Reader)  {
	processOutput(c.Stderr, notifier, r, c.ErrMaxBytes, c.ErrDelimiter)
}

func (c *SSHCommand) ProcessStdIn(notifier chan error, w io.WriteCloser) {
	processInput(c.Stdin, notifier, w)
}

func (s *SSHCommand) ProcessStdOut(notifier chan error, r io.Reader)  {
	processOutput(s.Stdout, notifier, r, s.OutMaxBytes, s.OutDelimiter)
}

func (s *SSHCommand) ProcessStdErr(notifier chan error, r io.Reader)  {
	processOutput(s.Stderr, notifier, r, s.ErrMaxBytes, s.ErrDelimiter)
}

func (s *SSHCommand) Close() {
	s.Session.Close()
	s.client.Close()
}

func execute(obj interface{}, cmd string) error {
	var innotifier chan error
	var outnotifier chan error
	var errnotifier chan error
	var ioerrs []string

	value := reflect.ValueOf(obj)
	vsession := value.Elem().FieldByName("Session")
	vstdin := value.Elem().FieldByName("Stdin")
	vstdout := value.Elem().FieldByName("Stdout")
	vstderr := value.Elem().FieldByName("Stderr")

	// Checking if a channel has been passed in to handle Stdout
	if !vstdin.IsNil() {
		innotifier = make(chan error)
		if method := vsession.MethodByName("StdinPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdIn"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(innotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdIn method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stdin: %s\n", values[1].Interface())
			}
		}
	}

	if !vstdout.IsNil() {
		outnotifier = make(chan error)
		if method := vsession.MethodByName("StdoutPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdOut"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(outnotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdOut method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stdout: %s\n", values[1].Interface())
			}
		}
	}
	if !vstderr.IsNil() {
		errnotifier = make(chan error)
		if method := vsession.MethodByName("StderrPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdErr"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(errnotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdOut method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stderr: %s\n", values[1].Interface())
			}
		}
	}

	// Run the command for the session
	if vstart := vsession.MethodByName("Start"); vstart.IsValid() {
		switch v := obj.(type) {
		case *Command:
			vstart.Call(nil)
		case *SSHCommand:
			vstart.Call([]reflect.Value{reflect.ValueOf(cmd)})
		default:
			return fmt.Errorf("Not a valid type, expected *Command or *SSHCommand but recevied %s", v)
		}
	}

	//Append stdin error if available
	if !vstdin.IsNil() {
		ioerrs = append(ioerrs, processErrors(innotifier)...)
	}

	//Append stdout errors if available
	if !vstdout.IsNil() {
		ioerrs = append(ioerrs, processErrors(outnotifier)...)
	}

	//Append stderr errors if available
	if !vstderr.IsNil() {
		ioerrs = append(ioerrs, processErrors(errnotifier)...)
	}

	//Iterate the errors and return them
	if ioerrs != nil && len(ioerrs) > 0 {
		errstr := "Errors found processing IO streams: \n"
		for i := 0; i < len(ioerrs); i++ {
			errstr = errstr + ioerrs[i]
		}
		return errors.New(errstr)
	}

	if vwait := vsession.MethodByName("Wait"); vwait.IsValid() {
		switch v := obj.(type) {
		case *Command:
			vwait.Call(nil)
		case *SSHCommand:
			vwait.Call(nil)
		default:
			return fmt.Errorf("Not a valid type, expected *Command or *SSHCommand but recevied %s", v)
		}
	}

	return nil
}

func processInput(in chan string, notifier chan error, w io.WriteCloser) {
	defer close(notifier)

	for {
		if in, ok := <-in; ok {
			input := strings.NewReader(in)
			if _, err := io.Copy(w, input); err != nil {
				notifier <-err
			}
		} else {
			return
		}
	}
}

func processOutput(out chan string, notifier chan error, r io.Reader, bytes int, delim byte) {
	defer close(notifier)
	defer close(out)

	bufr := bufio.NewReader(r)
	var str string
	var err error
	for {
		if bytes > 0 {
			var l int
			chars := make([]byte, bytes, bytes)
			l, err = io.ReadAtLeast(r,chars,bytes)
			str = string(chars[:l])
		} else {
			if delim == '\x00' {
				delim = '\n'
			}
			str, err = bufr.ReadString(delim)
		}
		if len(str) > 1 {
			out <-str
		}
		if err != nil {
			break
		}
	}
	if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
		notifier <-err
	}
}

func processErrors(notifier chan error) []string {
	var errlist []string
	for {
		err, ok := <-notifier
		if !ok {
			return errlist
		}
		errlist = append(errlist, err.Error())
	}
}

func loadPEM(filename string) (ssh.Signer, error) {
	privateKey, _ := ioutil.ReadFile(filename)
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}
	return signer, nil
}

type keyboardInteractive map[string]string

func (k *keyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) ([]string, error) {
	var answers []string
	for _, q := range questions {
		answers = append(answers, (*k)[q])
	}
	return answers, nil
}
<commit_msg>Updated import for golang crypto/ssh package to use the new location at golang.org<commit_after>package cmdr

import (
	"golang.org/x/crypto/ssh"
	"io"
	"io/ioutil"
	"bufio"
	"os/exec"
	"fmt"
	"reflect"
	"errors"
	"strings"
	"time"
)

func NewClientConfig(username, password, pemfile string) (*ssh.ClientConfig, error) {
	if username != "" && password != "" {
		answers := keyboardInteractive(map[string]string{"Password: ": password,})
		return &ssh.ClientConfig{
			User: username,
			Auth: []ssh.AuthMethod{
				ssh.KeyboardInteractive(answers.Challenge),
			},
		}, nil
	}

	if username != "" && pemfile != "" {
		signer, err := loadPEM(pemfile)
		if err != nil {
			return nil, err
		}

		return &ssh.ClientConfig{
			User: username,
			Auth: []ssh.AuthMethod{
				ssh.PublicKeys(signer),
			},
		}, nil
	}

	return nil, fmt.Errorf("Missing valid arguments, must pass a (username and password) or (username and pemfile).")
}

type Command struct {
	Session *exec.Cmd
	Stdin	chan string
	Stdout	chan string
	Stderr	chan string
	ErrDelimiter byte
	OutDelimiter byte
	ErrMaxBytes int
	OutMaxBytes int
	timeout		time.Duration
}

type SSHCommand struct {
	Command
	Config *ssh.ClientConfig
	Server string
	Session *ssh.Session
	client *ssh.Client
	timeout		time.Duration
}

func NewCommand(inchan, outchan, errchan chan string) (*Command, error) {
	return &Command{Stdin: inchan, Stdout: outchan, Stderr: errchan, timeout: 0}, nil
}

func NewSSHCommand(cfg *ssh.ClientConfig, server string, inchan, outchan, errchan chan string) (*SSHCommand, error) {
	return &SSHCommand{Config: cfg, Server: server, Command: Command{Stdin: inchan, Stdout: outchan, Stderr: errchan}, timeout: 0}, nil
}

func timeout(cmd *exec.Cmd, timeout time.Duration, done chan bool, err chan error) {
	if timeout > 0 {
		response := ""
		for {
			select {
			case <-time.After(timeout):
				cmd.Process.Kill()
				response = "Local command execution timeout reached, terminating command"
			case <-done:
				if len(response) > 1 {
					err <-fmt.Errorf(response)
				} else {
					close(err)
				}
				return
			}
		}
	} else {
		err <-nil
	}
}

func timeout_ssh(client *ssh.Client, timeout time.Duration, done chan bool, err chan error) {
	if timeout > 0 {
		response := ""
		for {
			select {
			case <-time.After(timeout):
				client.Conn.Close()
				response = "Remote SSH command execution timeout reached, terminating command"
			case <-done:
				if len(response) > 1 {
					err <-fmt.Errorf(response)
				} else {
					close(err)
				}
				return
			}
		}
	} else {
		err <-nil
	}
}

func (c *Command) Execute(cmd string, args ...string) error {
	c.Session = exec.Command(cmd, args...)
	done := make(chan bool, 1)
	errchan := make(chan error, 1)
	defer close(done)
	go timeout(c.Session, c.timeout, done, errchan)

	if err := execute(c, ""); err != nil {
		fmt.Printf("Execute Error: %s\n", err)
		done <-true
		<-errchan
		return err
	}

	done <-true
	err := <-errchan
	return err
}

func (s *SSHCommand) Execute(cmd string) (err error) {
	s.client, err = ssh.Dial("tcp", s.Server, s.Config)
	if err != nil {
		return err
	}
	s.Session, err = s.client.NewSession()
	if err != nil {
		return err
	}

	done := make(chan bool, 1)
	errchan := make(chan error, 1)
	defer close(done)
	go timeout_ssh(s.client, s.timeout, done, errchan)

	if err = execute(s, cmd); err != nil {
		done <-true
		<-errchan
		return err
	}

	done <-true
	err = <-errchan
	return err
}

func (c *Command) SetTimeout(timeout time.Duration) {
	c.timeout = timeout
}

func (s *SSHCommand) SetTimeout(timeout time.Duration) {
	s.timeout = timeout
}

func (c *Command) ProcessStdIn(notifier chan error, w io.WriteCloser) {
	processInput(c.Stdin, notifier, w)
}

func (c *Command) ProcessStdOut(notifier chan error, r io.Reader)  {
	processOutput(c.Stdout, notifier, r, c.OutMaxBytes, c.OutDelimiter)
}

func (c *Command) ProcessStdErr(notifier chan error, r io.Reader)  {
	processOutput(c.Stderr, notifier, r, c.ErrMaxBytes, c.ErrDelimiter)
}

func (c *SSHCommand) ProcessStdIn(notifier chan error, w io.WriteCloser) {
	processInput(c.Stdin, notifier, w)
}

func (s *SSHCommand) ProcessStdOut(notifier chan error, r io.Reader)  {
	processOutput(s.Stdout, notifier, r, s.OutMaxBytes, s.OutDelimiter)
}

func (s *SSHCommand) ProcessStdErr(notifier chan error, r io.Reader)  {
	processOutput(s.Stderr, notifier, r, s.ErrMaxBytes, s.ErrDelimiter)
}

func (s *SSHCommand) Close() {
	s.Session.Close()
	s.client.Close()
}

func execute(obj interface{}, cmd string) error {
	var innotifier chan error
	var outnotifier chan error
	var errnotifier chan error
	var ioerrs []string

	value := reflect.ValueOf(obj)
	vsession := value.Elem().FieldByName("Session")
	vstdin := value.Elem().FieldByName("Stdin")
	vstdout := value.Elem().FieldByName("Stdout")
	vstderr := value.Elem().FieldByName("Stderr")

	// Checking if a channel has been passed in to handle Stdout
	if !vstdin.IsNil() {
		innotifier = make(chan error)
		if method := vsession.MethodByName("StdinPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdIn"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(innotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdIn method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stdin: %s\n", values[1].Interface())
			}
		}
	}

	if !vstdout.IsNil() {
		outnotifier = make(chan error)
		if method := vsession.MethodByName("StdoutPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdOut"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(outnotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdOut method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stdout: %s\n", values[1].Interface())
			}
		}
	}
	if !vstderr.IsNil() {
		errnotifier = make(chan error)
		if method := vsession.MethodByName("StderrPipe"); method.IsValid() {
			values := method.Call(nil)
			if values[1].IsNil() {
				pipe := values[0].Interface()
				if processMethod := value.MethodByName("ProcessStdErr"); processMethod.IsValid() {
					go processMethod.Call([]reflect.Value{reflect.ValueOf(errnotifier), reflect.ValueOf(pipe)})
				} else {
					return fmt.Errorf("ProcessStdOut method not found\n")
				}
			} else {
				return fmt.Errorf("An error occurred connecting up to Stderr: %s\n", values[1].Interface())
			}
		}
	}

	// Run the command for the session
	if vstart := vsession.MethodByName("Start"); vstart.IsValid() {
		switch v := obj.(type) {
		case *Command:
			vstart.Call(nil)
		case *SSHCommand:
			vstart.Call([]reflect.Value{reflect.ValueOf(cmd)})
		default:
			return fmt.Errorf("Not a valid type, expected *Command or *SSHCommand but recevied %s", v)
		}
	}

	//Append stdin error if available
	if !vstdin.IsNil() {
		ioerrs = append(ioerrs, processErrors(innotifier)...)
	}

	//Append stdout errors if available
	if !vstdout.IsNil() {
		ioerrs = append(ioerrs, processErrors(outnotifier)...)
	}

	//Append stderr errors if available
	if !vstderr.IsNil() {
		ioerrs = append(ioerrs, processErrors(errnotifier)...)
	}

	//Iterate the errors and return them
	if ioerrs != nil && len(ioerrs) > 0 {
		errstr := "Errors found processing IO streams: \n"
		for i := 0; i < len(ioerrs); i++ {
			errstr = errstr + ioerrs[i]
		}
		return errors.New(errstr)
	}

	if vwait := vsession.MethodByName("Wait"); vwait.IsValid() {
		switch v := obj.(type) {
		case *Command:
			vwait.Call(nil)
		case *SSHCommand:
			vwait.Call(nil)
		default:
			return fmt.Errorf("Not a valid type, expected *Command or *SSHCommand but recevied %s", v)
		}
	}

	return nil
}

func processInput(in chan string, notifier chan error, w io.WriteCloser) {
	defer close(notifier)

	for {
		if in, ok := <-in; ok {
			input := strings.NewReader(in)
			if _, err := io.Copy(w, input); err != nil {
				notifier <-err
			}
		} else {
			return
		}
	}
}

func processOutput(out chan string, notifier chan error, r io.Reader, bytes int, delim byte) {
	defer close(notifier)
	defer close(out)

	bufr := bufio.NewReader(r)
	var str string
	var err error
	for {
		if bytes > 0 {
			var l int
			chars := make([]byte, bytes, bytes)
			l, err = io.ReadAtLeast(r,chars,bytes)
			str = string(chars[:l])
		} else {
			if delim == '\x00' {
				delim = '\n'
			}
			str, err = bufr.ReadString(delim)
		}
		if len(str) > 1 {
			out <-str
		}
		if err != nil {
			break
		}
	}
	if !(err == io.EOF || err == io.ErrUnexpectedEOF) {
		notifier <-err
	}
}

func processErrors(notifier chan error) []string {
	var errlist []string
	for {
		err, ok := <-notifier
		if !ok {
			return errlist
		}
		errlist = append(errlist, err.Error())
	}
}

func loadPEM(filename string) (ssh.Signer, error) {
	privateKey, _ := ioutil.ReadFile(filename)
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}
	return signer, nil
}

type keyboardInteractive map[string]string

func (k *keyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) ([]string, error) {
	var answers []string
	for _, q := range questions {
		answers = append(answers, (*k)[q])
	}
	return answers, nil
}
<|endoftext|>


<commit_before>package velox

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/donovanhide/eventsource"
	"github.com/gorilla/websocket"
)

//Conn represents a single live connection being synchronised.
//Its ID is the the connection's remote address.
type Conn interface {
	ID() string
	Connected() bool
	Wait()
	Push()
	Close() error
}

//NOTE transport should only be used in this file!
type transport interface {
	connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error
	send(upd *update) error
	close() error
}

type conn struct {
	transport  transport
	state      *State
	connected  bool
	id         string
	first      uint32
	uptime     time.Time
	version    int64
	sendingMut sync.Mutex
	waiter     sync.WaitGroup
}

func (c *conn) ID() string {
	return c.id
}

func (c *conn) Connected() bool {
	return c.connected
}

//Wait will block until the connection is closed.
func (c *conn) Wait() {
	c.waiter.Wait()
}

//Push will the current state only to this client.
//Blocks until push is complete.
func (c *conn) Push() {
	c.state.pushTo(c)
}

//Force close the connection.
func (c *conn) Close() error {
	if c.transport != nil {
		err := c.transport.close()
		c.connected = false
		c.transport = nil
		return err
	}
	return nil
}

//connect using the provided transport
//and block until successfully connected
func (c *conn) connect(w http.ResponseWriter, r *http.Request) error {
	//choose transport
	if r.Header.Get("Accept") == "text/event-stream" {
		c.transport = &evtSrcTrans{}
	} else if r.Header.Get("Upgrade") == "websocket" {
		c.transport = &wsTrans{}
	} else {
		return fmt.Errorf("Invalid sync request")
	}
	//connect to client over set transport
	c.waiter.Add(1)
	isConnected := make(chan error)
	go func() {
		//connect, and then:
		// * send on channel success/failed connection
		// * return when disconnected
		if err := c.transport.connect(w, r, isConnected); err != nil {
			//transport error after connection TODO(jpillora): log optionally
		}
		//disconnected, done waiting
		c.Close()
		c.waiter.Done()
	}()
	//wait here until transport completes connection
	if err := <-isConnected; err != nil {
		return err
	}
	//once successfully connected, ping loop (every 25s, browser timesout after 30s)
	c.connected = true
	go func() {
		for {
			time.Sleep(25 * time.Second)
			if !c.connected {
				return
			}
			c.send(&update{Ping: true})
		}
	}()
	//now connected, consumer should connection.Wait()
	return nil
}

//send to connection, ensure only 1 concurrent sender
func (c *conn) send(upd *update) error {
	if !c.Connected() {
		return errors.New("not connected")
	}
	c.sendingMut.Lock()
	defer c.sendingMut.Unlock()
	return c.transport.send(upd)
}

//=========================

var defaultUpgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type wsTrans struct {
	conn *websocket.Conn
}

func (ws *wsTrans) connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error {
	conn, err := defaultUpgrader.Upgrade(w, r, nil)
	if err != nil {
		isConnected <- err
		return fmt.Errorf("cannot upgrade connection: %s", err)
	}
	ws.conn = conn
	isConnected <- nil
	//block on connection
	for {
		//msgType, msgBytes, err
		if _, _, err := conn.ReadMessage(); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
	}
	return nil
}

func (ws *wsTrans) send(upd *update) error {
	return ws.conn.WriteJSON(upd)
}

func (ws *wsTrans) close() error {
	return ws.conn.Close()
}

//=========================

type evtSrcTrans struct {
	s *eventsource.Server
}

//evtSrcWriter is a hack to
//watch when connection is ready
type evtSrcWriter struct {
	isFlushed   bool
	isConnected chan error
	http.ResponseWriter
}

func (ew *evtSrcWriter) CloseNotify() <-chan bool {
	return ew.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

//HACK: uses eventsource's call to flush as a signal that its connected
func (ew *evtSrcWriter) Flush() {
	if !ew.isFlushed {
		ew.isConnected <- nil
	}
	ew.isFlushed = true
	f, ok := ew.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

func (es *evtSrcTrans) connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error {
	es.s = eventsource.NewServer()
	if !strings.Contains(w.Header().Get("Content-Encoding"), "gzip") {
		es.s.Gzip = true
	}
	ew := &evtSrcWriter{false, isConnected, w}
	es.s.Handler("events").ServeHTTP(ew, r)
	return nil
}

func (es *evtSrcTrans) send(upd *update) error {
	es.s.Publish([]string{"events"}, upd)
	return nil
}

func (es *evtSrcTrans) close() error {
	if es.s != nil {
		es.s.Close()
	}
	return nil
}
<commit_msg>fix panic<commit_after>package velox

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/donovanhide/eventsource"
	"github.com/gorilla/websocket"
)

//Conn represents a single live connection being synchronised.
//Its ID is the the connection's remote address.
type Conn interface {
	ID() string
	Connected() bool
	Wait()
	Push()
	Close() error
}

//NOTE transport should only be used in this file!
type transport interface {
	connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error
	send(upd *update) error
	close() error
}

type conn struct {
	transport  transport
	state      *State
	connected  bool
	id         string
	first      uint32
	uptime     time.Time
	version    int64
	sendingMut sync.Mutex
	waiter     sync.WaitGroup
}

func (c *conn) ID() string {
	return c.id
}

func (c *conn) Connected() bool {
	return c.connected
}

//Wait will block until the connection is closed.
func (c *conn) Wait() {
	c.waiter.Wait()
}

//Push will the current state only to this client.
//Blocks until push is complete.
func (c *conn) Push() {
	c.state.pushTo(c)
}

//Force close the connection.
func (c *conn) Close() error {
	if c.transport != nil {
		err := c.transport.close()
		c.connected = false
		c.transport = nil
		return err
	}
	return nil
}

//connect using the provided transport
//and block until successfully connected
func (c *conn) connect(w http.ResponseWriter, r *http.Request) error {
	//choose transport
	if r.Header.Get("Accept") == "text/event-stream" {
		c.transport = &evtSrcTrans{}
	} else if r.Header.Get("Upgrade") == "websocket" {
		c.transport = &wsTrans{}
	} else {
		return fmt.Errorf("Invalid sync request")
	}
	//connect to client over set transport
	c.waiter.Add(1)
	isConnected := make(chan error)
	go func() {
		//connect, and then:
		// * send on channel success/failed connection
		// * return when disconnected
		if err := c.transport.connect(w, r, isConnected); err != nil {
			//transport error after connection TODO(jpillora): log optionally
		}
		//disconnected, done waiting
		c.Close()
		c.waiter.Done()
	}()
	//wait here until transport completes connection
	if err := <-isConnected; err != nil {
		return err
	}
	//once successfully connected, ping loop (every 25s, browser timesout after 30s)
	c.connected = true
	go func() {
		for {
			time.Sleep(25 * time.Second)
			if !c.connected {
				return
			}
			c.send(&update{Ping: true})
		}
	}()
	//now connected, consumer should connection.Wait()
	return nil
}

//send to connection, ensure only 1 concurrent sender
func (c *conn) send(upd *update) error {
	if c.transport == nil {
		return errors.New("not connected")
	}
	c.sendingMut.Lock()
	defer c.sendingMut.Unlock()
	return c.transport.send(upd)
}

//=========================

var defaultUpgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type wsTrans struct {
	conn *websocket.Conn
}

func (ws *wsTrans) connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error {
	conn, err := defaultUpgrader.Upgrade(w, r, nil)
	if err != nil {
		isConnected <- err
		return fmt.Errorf("cannot upgrade connection: %s", err)
	}
	ws.conn = conn
	isConnected <- nil
	//block on connection
	for {
		//msgType, msgBytes, err
		if _, _, err := conn.ReadMessage(); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
	}
	return nil
}

func (ws *wsTrans) send(upd *update) error {
	return ws.conn.WriteJSON(upd)
}

func (ws *wsTrans) close() error {
	return ws.conn.Close()
}

//=========================

type evtSrcTrans struct {
	s *eventsource.Server
}

//evtSrcWriter is a hack to
//watch when connection is ready
type evtSrcWriter struct {
	isFlushed   bool
	isConnected chan error
	http.ResponseWriter
}

func (ew *evtSrcWriter) CloseNotify() <-chan bool {
	return ew.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

//HACK: uses eventsource's call to flush as a signal that its connected
func (ew *evtSrcWriter) Flush() {
	if !ew.isFlushed {
		ew.isConnected <- nil
	}
	ew.isFlushed = true
	f, ok := ew.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

func (es *evtSrcTrans) connect(w http.ResponseWriter, r *http.Request, isConnected chan error) error {
	es.s = eventsource.NewServer()
	if !strings.Contains(w.Header().Get("Content-Encoding"), "gzip") {
		es.s.Gzip = true
	}
	ew := &evtSrcWriter{false, isConnected, w}
	es.s.Handler("events").ServeHTTP(ew, r)
	return nil
}

func (es *evtSrcTrans) send(upd *update) error {
	es.s.Publish([]string{"events"}, upd)
	return nil
}

func (es *evtSrcTrans) close() error {
	if es.s != nil {
		es.s.Close()
	}
	return nil
}
<|endoftext|>