<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"errors"
	"reflect"
	"sort"

	"github.com/keybase/client/go/protocol/keybase1"
)

// Handle uniquely identifies top-level folders by readers and
// writers.
//
// TODO: Have separate types for writers vs. readers.
type Handle struct {
	Writers           []keybase1.UserOrTeamID    `codec:"w,omitempty"`
	Readers           []keybase1.UserOrTeamID    `codec:"r,omitempty"`
	UnresolvedWriters []keybase1.SocialAssertion `codec:"uw,omitempty"`
	UnresolvedReaders []keybase1.SocialAssertion `codec:"ur,omitempty"`
	ConflictInfo      *HandleExtension           `codec:"ci,omitempty"`
	FinalizedInfo     *HandleExtension           `codec:"fi,omitempty"`
}

// errNoWriters is the error returned by MakeHandle if it is
// passed an empty list of writers.
var errNoWriters = errors.New("Cannot make TLF handle with no writers; need rekey?")

// errInvalidWriter is the error returned by MakeHandle if it
// is passed an invalid writer.
var errInvalidWriter = errors.New("Cannot make TLF handle with invalid writer")

// errInvalidReader is the error returned by MakeHandle if it
// is passed an invalid reader.
var errInvalidReader = errors.New("Cannot make TLF handle with invalid reader")

// UIDList can be used to lexicographically sort UserOrTeamIDs.
type UIDList []keybase1.UserOrTeamID

func (u UIDList) Len() int {
	return len(u)
}

func (u UIDList) Less(i, j int) bool {
	return u[i].Less(u[j])
}

func (u UIDList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// SocialAssertionList can be used to lexicographically sort SocialAssertions.
type SocialAssertionList []keybase1.SocialAssertion

func (u SocialAssertionList) Len() int {
	return len(u)
}

func (u SocialAssertionList) Less(i, j int) bool {
	si := u[i].String()
	sj := u[j].String()
	return si < sj
}

func (u SocialAssertionList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// MakeHandle creates a Handle from the given list of
// readers and writers. If the given reader list contains just
// keybase1.PUBLIC_UID, then the returned handle will be for a public
// folder. Otherwise, it will be private. PUBLIC_UID shouldn't be in
// any list in any other case.
func MakeHandle(
	writers, readers []keybase1.UserOrTeamID,
	unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) (Handle, error) {
	if len(writers) == 0 {
		return Handle{}, errNoWriters
	}

	if writers[0].IsTeamOrSubteam() {
		// Right now we only support single-team private TLFs.
		if len(writers) > 1 || len(unresolvedWriters) != 0 {
			return Handle{}, errInvalidWriter
		} else if len(readers) != 0 || len(unresolvedReaders) != 0 {
			return Handle{}, errInvalidReader
		}
	}

	for i, w := range writers {
		if w == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidWriter
		} else if i > 0 && w.IsTeamOrSubteam() {
			return Handle{}, errInvalidWriter
		}
	}

	// If we have more than one reader, none of them should be the
	// public UID.  And no readers should be a team.
	checkPublic := (len(readers) + len(unresolvedReaders)) > 1
	for _, r := range readers {
		if checkPublic && r == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidReader
		} else if r.IsTeamOrSubteam() {
			return Handle{}, errInvalidReader
		}
	}

	// TODO: Check for overlap between readers and writers, and
	// for duplicates.

	writersCopy := make([]keybase1.UserOrTeamID, len(writers))
	copy(writersCopy, writers)
	sort.Sort(UIDList(writersCopy))

	var readersCopy []keybase1.UserOrTeamID
	if len(readers) > 0 {
		readersCopy = make([]keybase1.UserOrTeamID, len(readers))
		copy(readersCopy, readers)
		sort.Sort(UIDList(readersCopy))
	}

	var unresolvedWritersCopy []keybase1.SocialAssertion
	if len(unresolvedWriters) > 0 {
		unresolvedWritersCopy = make([]keybase1.SocialAssertion, len(unresolvedWriters))
		copy(unresolvedWritersCopy, unresolvedWriters)
		sort.Sort(SocialAssertionList(unresolvedWritersCopy))
	}

	var unresolvedReadersCopy []keybase1.SocialAssertion
	if len(unresolvedReaders) > 0 {
		unresolvedReadersCopy = make([]keybase1.SocialAssertion, len(unresolvedReaders))
		copy(unresolvedReadersCopy, unresolvedReaders)
		sort.Sort(SocialAssertionList(unresolvedReadersCopy))
	}

	conflictInfo, finalizedInfo := HandleExtensionList(extensions).Splat()

	return Handle{
		Writers:           writersCopy,
		Readers:           readersCopy,
		UnresolvedWriters: unresolvedWritersCopy,
		UnresolvedReaders: unresolvedReadersCopy,
		ConflictInfo:      conflictInfo,
		FinalizedInfo:     finalizedInfo,
	}, nil
}

// IsBackedByTeam returns true if h represents a TLF backed by a team. It could
// be either a SingleTeam TLF or a private/public TLF backed by an implicit
// team.
func (h Handle) IsBackedByTeam() bool {
	if len(h.Writers) != 1 ||
		len(h.Readers) != 0 ||
		len(h.UnresolvedReaders) != 0 ||
		len(h.UnresolvedWriters) != 0 {
		return false
	}
	return h.Writers[0].IsTeamOrSubteam()
}

// Type returns the type of TLF this Handle represents.
func (h Handle) Type() Type {
	if len(h.Readers) == 1 &&
		h.Readers[0].Equal(keybase1.PublicUID.AsUserOrTeam()) {
		return Public
	} else if len(h.Writers) == 1 && h.Writers[0].IsTeamOrSubteam() {
		return SingleTeam
	}
	return Private
}

// TypeForKeying returns the keying type for the handle h.
func (h Handle) TypeForKeying() KeyingType {
	if h.IsBackedByTeam() {
		return TeamKeying
	}
	return h.Type().ToKeyingType()
}

func (h Handle) findUserInList(user keybase1.UserOrTeamID,
	users []keybase1.UserOrTeamID) bool {
	for _, u := range users {
		if u == user {
			return true
		}
	}
	return false
}

// IsWriter returns whether or not the given user is a writer for the
// top-level folder represented by this Handle.
func (h Handle) IsWriter(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsWriter() for a single team TLF")
	}
	return h.findUserInList(user, h.Writers)
}

// IsReader returns whether or not the given user is a reader for the
// top-level folder represented by this Handle.
func (h Handle) IsReader(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsReader() for a single team TLF")
	}
	return h.TypeForKeying() == PublicKeying ||
		h.findUserInList(user, h.Readers) ||
		h.IsWriter(user)
}

// ResolvedUsers returns the concatenation of h.Writers and h.Readers,
// except if the handle is public, the returned list won't contain
// PUBLIC_UID.
func (h Handle) ResolvedUsers() []keybase1.UserOrTeamID {
	var resolvedUsers []keybase1.UserOrTeamID
	resolvedUsers = append(resolvedUsers, h.Writers...)
	if h.TypeForKeying() == PrivateKeying {
		resolvedUsers = append(resolvedUsers, h.Readers...)
	}
	return resolvedUsers
}

// HasUnresolvedUsers returns true if this handle has any unresolved
// writers or readers.
func (h Handle) HasUnresolvedUsers() bool {
	return len(h.UnresolvedWriters) > 0 || len(h.UnresolvedReaders) > 0
}

// UnresolvedUsers returns the concatenation of h.UnresolvedWriters
// and h.UnresolvedReaders.
func (h Handle) UnresolvedUsers() []keybase1.SocialAssertion {
	var unresolvedUsers []keybase1.SocialAssertion
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedWriters...)
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedReaders...)
	return unresolvedUsers
}

func uidSliceToSet(s []keybase1.UserOrTeamID) map[keybase1.UserOrTeamID]bool {
	m := make(map[keybase1.UserOrTeamID]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func assertionSliceToSet(s []keybase1.SocialAssertion) map[keybase1.SocialAssertion]bool {
	m := make(map[keybase1.SocialAssertion]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func resolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID,
	unresolved []keybase1.SocialAssertion, resolved []keybase1.UserOrTeamID) (
	map[keybase1.UserOrTeamID]bool, []keybase1.SocialAssertion) {
	resolvedMap := uidSliceToSet(resolved)
	unresolvedMap := assertionSliceToSet(unresolved)
	for a, u := range assertions {
		if unresolvedMap[a] {
			resolvedMap[u.AsUserOrTeam()] = true
			delete(unresolvedMap, a)
		}
	}
	return resolvedMap, assertionSetToSlice(unresolvedMap)
}

func uidSetToSlice(m map[keybase1.UserOrTeamID]bool) (
	s []keybase1.UserOrTeamID) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

func assertionSetToSlice(m map[keybase1.SocialAssertion]bool) (s []keybase1.SocialAssertion) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

// ResolveAssertions creates a new Handle given an existing one with
// while resolving the passed assertions.
func (h Handle) ResolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID) Handle {
	if len(assertions) == 0 || (len(h.UnresolvedWriters) == 0 && len(h.UnresolvedReaders) == 0) || h.IsFinal() {
		return h
	}
	var resolvedWriters, resolvedReaders map[keybase1.UserOrTeamID]bool
	resolvedWriters, h.UnresolvedWriters = resolveAssertions(assertions, h.UnresolvedWriters, h.Writers)
	resolvedReaders, h.UnresolvedReaders = resolveAssertions(assertions, h.UnresolvedReaders, h.Readers)
	h.Writers = uidSetToSlice(resolvedWriters)
	for _, u := range h.Writers {
		delete(resolvedReaders, u)
	}
	h.Readers = uidSetToSlice(resolvedReaders)
	sort.Sort(UIDList(h.Writers))
	sort.Sort(UIDList(h.Readers))
	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
	return h
}

// Extensions returns a list of extensions for the given handle.
func (h Handle) Extensions() (extensions []HandleExtension) {
	if h.ConflictInfo != nil {
		extensions = append(extensions, *h.ConflictInfo)
	}
	if h.FinalizedInfo != nil {
		extensions = append(extensions, *h.FinalizedInfo)
	}
	return extensions
}

// IsFinal returns true if the handle has been finalized.
func (h Handle) IsFinal() bool {
	return h.FinalizedInfo != nil
}

// IsConflict returns true if the handle is a conflict handle.
func (h Handle) IsConflict() bool {
	return h.ConflictInfo != nil
}

// DeepEqual returns true if the handle is equal to another handle.
func (h Handle) DeepEqual(other Handle) bool {
	sort.Sort(UIDList(h.Writers))
	sort.Sort(UIDList(h.Readers))
	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
	sort.Sort(UIDList(other.Writers))
	sort.Sort(UIDList(other.Readers))
	sort.Sort(SocialAssertionList(other.UnresolvedWriters))
	sort.Sort(SocialAssertionList(other.UnresolvedReaders))

	return reflect.DeepEqual(h, other)
}
<commit_msg>tlf.handle: PR feedback to rewrite the whole thing. THE WHOLE THING. Is better now anyway.<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package tlf

import (
	"errors"
	"reflect"
	"sort"

	"github.com/keybase/client/go/protocol/keybase1"
)

// Handle uniquely identified top-level folders by readers and writers.
//
// NOTE: if you change this type, ensure you update the `NumField` check in
// `MakeHandle`, and that the new fields are correctly checked for equality in
// `DeepEqual`.
// TODO: Have separate types for writers vs. readers.
type Handle struct {
	Writers           []keybase1.UserOrTeamID    `codec:"w,omitempty"`
	Readers           []keybase1.UserOrTeamID    `codec:"r,omitempty"`
	UnresolvedWriters []keybase1.SocialAssertion `codec:"uw,omitempty"`
	UnresolvedReaders []keybase1.SocialAssertion `codec:"ur,omitempty"`
	ConflictInfo      *HandleExtension           `codec:"ci,omitempty"`
	FinalizedInfo     *HandleExtension           `codec:"fi,omitempty"`

	// caching field to track whether we've sorted the slices.
	sorted bool `codec:"-"`
}

// errNoWriters is the error returned by MakeHandle if it is
// passed an empty list of writers.
var errNoWriters = errors.New("Cannot make TLF handle with no writers; need rekey?")

// errInvalidWriter is the error returned by MakeHandle if it
// is passed an invalid writer.
var errInvalidWriter = errors.New("Cannot make TLF handle with invalid writer")

// errInvalidReader is the error returned by MakeHandle if it
// is passed an invalid reader.
var errInvalidReader = errors.New("Cannot make TLF handle with invalid reader")

// UIDList can be used to lexicographically sort UserOrTeamIDs.
type UIDList []keybase1.UserOrTeamID

func (u UIDList) Len() int {
	return len(u)
}

func (u UIDList) Less(i, j int) bool {
	return u[i].Less(u[j])
}

func (u UIDList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// SocialAssertionList can be used to lexicographically sort SocialAssertions.
type SocialAssertionList []keybase1.SocialAssertion

func (u SocialAssertionList) Len() int {
	return len(u)
}

func (u SocialAssertionList) Less(i, j int) bool {
	si := u[i].String()
	sj := u[j].String()
	return si < sj
}

func (u SocialAssertionList) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}

// MakeHandle creates a Handle from the given list of
// readers and writers. If the given reader list contains just
// keybase1.PUBLIC_UID, then the returned handle will be for a public
// folder. Otherwise, it will be private. PUBLIC_UID shouldn't be in
// any list in any other case.
func MakeHandle(
	writers, readers []keybase1.UserOrTeamID,
	unresolvedWriters, unresolvedReaders []keybase1.SocialAssertion,
	extensions []HandleExtension) (Handle, error) {
	if reflect.ValueOf(Handle{}).NumField() != 7 {
		panic(errors.New(
			"Unexpected number of fields in Handle; please update the check " +
				"above and ensure that Handle.DeepEqual() accounts for the " +
				"new field"))
	}
	if len(writers) == 0 {
		return Handle{}, errNoWriters
	}

	if writers[0].IsTeamOrSubteam() {
		// Right now we only support single-team private TLFs.
		if len(writers) > 1 || len(unresolvedWriters) != 0 {
			return Handle{}, errInvalidWriter
		} else if len(readers) != 0 || len(unresolvedReaders) != 0 {
			return Handle{}, errInvalidReader
		}
	}

	for i, w := range writers {
		if w == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidWriter
		} else if i > 0 && w.IsTeamOrSubteam() {
			return Handle{}, errInvalidWriter
		}
	}

	// If we have more than one reader, none of them should be the
	// public UID.  And no readers should be a team.
	checkPublic := (len(readers) + len(unresolvedReaders)) > 1
	for _, r := range readers {
		if checkPublic && r == keybase1.PUBLIC_UID {
			return Handle{}, errInvalidReader
		} else if r.IsTeamOrSubteam() {
			return Handle{}, errInvalidReader
		}
	}

	// TODO: Check for overlap between readers and writers, and
	// for duplicates.

	writersCopy := make([]keybase1.UserOrTeamID, len(writers))
	copy(writersCopy, writers)
	sort.Sort(UIDList(writersCopy))

	var readersCopy []keybase1.UserOrTeamID
	if len(readers) > 0 {
		readersCopy = make([]keybase1.UserOrTeamID, len(readers))
		copy(readersCopy, readers)
		sort.Sort(UIDList(readersCopy))
	}

	var unresolvedWritersCopy []keybase1.SocialAssertion
	if len(unresolvedWriters) > 0 {
		unresolvedWritersCopy = make([]keybase1.SocialAssertion, len(unresolvedWriters))
		copy(unresolvedWritersCopy, unresolvedWriters)
		sort.Sort(SocialAssertionList(unresolvedWritersCopy))
	}

	var unresolvedReadersCopy []keybase1.SocialAssertion
	if len(unresolvedReaders) > 0 {
		unresolvedReadersCopy = make([]keybase1.SocialAssertion, len(unresolvedReaders))
		copy(unresolvedReadersCopy, unresolvedReaders)
		sort.Sort(SocialAssertionList(unresolvedReadersCopy))
	}

	conflictInfo, finalizedInfo := HandleExtensionList(extensions).Splat()

	return Handle{
		Writers:           writersCopy,
		Readers:           readersCopy,
		UnresolvedWriters: unresolvedWritersCopy,
		UnresolvedReaders: unresolvedReadersCopy,
		ConflictInfo:      conflictInfo,
		FinalizedInfo:     finalizedInfo,
	}, nil
}

// IsBackedByTeam returns true if h represents a TLF backed by a team. It could
// be either a SingleTeam TLF or a private/public TLF backed by an implicit
// team.
func (h Handle) IsBackedByTeam() bool {
	if len(h.Writers) != 1 ||
		len(h.Readers) != 0 ||
		len(h.UnresolvedReaders) != 0 ||
		len(h.UnresolvedWriters) != 0 {
		return false
	}
	return h.Writers[0].IsTeamOrSubteam()
}

// Type returns the type of TLF this Handle represents.
func (h Handle) Type() Type {
	if len(h.Readers) == 1 &&
		h.Readers[0].Equal(keybase1.PublicUID.AsUserOrTeam()) {
		return Public
	} else if len(h.Writers) == 1 && h.Writers[0].IsTeamOrSubteam() {
		return SingleTeam
	}
	return Private
}

// TypeForKeying returns the keying type for the handle h.
func (h Handle) TypeForKeying() KeyingType {
	if h.IsBackedByTeam() {
		return TeamKeying
	}
	return h.Type().ToKeyingType()
}

func (h Handle) findUserInList(user keybase1.UserOrTeamID,
	users []keybase1.UserOrTeamID) bool {
	for _, u := range users {
		if u == user {
			return true
		}
	}
	return false
}

// IsWriter returns whether or not the given user is a writer for the
// top-level folder represented by this Handle.
func (h Handle) IsWriter(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsWriter() for a single team TLF")
	}
	return h.findUserInList(user, h.Writers)
}

// IsReader returns whether or not the given user is a reader for the
// top-level folder represented by this Handle.
func (h Handle) IsReader(user keybase1.UserOrTeamID) bool {
	if h.TypeForKeying() == TeamKeying {
		panic("Can't call Handle.IsReader() for a single team TLF")
	}
	return h.TypeForKeying() == PublicKeying ||
		h.findUserInList(user, h.Readers) ||
		h.IsWriter(user)
}

// ResolvedUsers returns the concatenation of h.Writers and h.Readers,
// except if the handle is public, the returned list won't contain
// PUBLIC_UID.
func (h Handle) ResolvedUsers() []keybase1.UserOrTeamID {
	var resolvedUsers []keybase1.UserOrTeamID
	resolvedUsers = append(resolvedUsers, h.Writers...)
	if h.TypeForKeying() == PrivateKeying {
		resolvedUsers = append(resolvedUsers, h.Readers...)
	}
	return resolvedUsers
}

// HasUnresolvedUsers returns true if this handle has any unresolved
// writers or readers.
func (h Handle) HasUnresolvedUsers() bool {
	return len(h.UnresolvedWriters) > 0 || len(h.UnresolvedReaders) > 0
}

// UnresolvedUsers returns the concatenation of h.UnresolvedWriters
// and h.UnresolvedReaders.
func (h Handle) UnresolvedUsers() []keybase1.SocialAssertion {
	var unresolvedUsers []keybase1.SocialAssertion
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedWriters...)
	unresolvedUsers = append(unresolvedUsers, h.UnresolvedReaders...)
	return unresolvedUsers
}

func uidSliceToSet(s []keybase1.UserOrTeamID) map[keybase1.UserOrTeamID]bool {
	m := make(map[keybase1.UserOrTeamID]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func assertionSliceToSet(s []keybase1.SocialAssertion) map[keybase1.SocialAssertion]bool {
	m := make(map[keybase1.SocialAssertion]bool, len(s))
	for _, u := range s {
		m[u] = true
	}
	return m
}

func resolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID,
	unresolved []keybase1.SocialAssertion, resolved []keybase1.UserOrTeamID) (
	map[keybase1.UserOrTeamID]bool, []keybase1.SocialAssertion) {
	resolvedMap := uidSliceToSet(resolved)
	unresolvedMap := assertionSliceToSet(unresolved)
	for a, u := range assertions {
		if unresolvedMap[a] {
			resolvedMap[u.AsUserOrTeam()] = true
			delete(unresolvedMap, a)
		}
	}
	return resolvedMap, assertionSetToSlice(unresolvedMap)
}

func uidSetToSlice(m map[keybase1.UserOrTeamID]bool) (
	s []keybase1.UserOrTeamID) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

func assertionSetToSlice(m map[keybase1.SocialAssertion]bool) (s []keybase1.SocialAssertion) {
	for u := range m {
		s = append(s, u)
	}
	return s
}

// ResolveAssertions creates a new Handle given an existing one with
// while resolving the passed assertions.
func (h Handle) ResolveAssertions(
	assertions map[keybase1.SocialAssertion]keybase1.UID) Handle {
	if len(assertions) == 0 || (len(h.UnresolvedWriters) == 0 && len(h.UnresolvedReaders) == 0) || h.IsFinal() {
		return h
	}
	var resolvedWriters, resolvedReaders map[keybase1.UserOrTeamID]bool
	resolvedWriters, h.UnresolvedWriters = resolveAssertions(assertions, h.UnresolvedWriters, h.Writers)
	resolvedReaders, h.UnresolvedReaders = resolveAssertions(assertions, h.UnresolvedReaders, h.Readers)
	h.Writers = uidSetToSlice(resolvedWriters)
	for _, u := range h.Writers {
		delete(resolvedReaders, u)
	}
	h.Readers = uidSetToSlice(resolvedReaders)
	sort.Sort(UIDList(h.Writers))
	sort.Sort(UIDList(h.Readers))
	sort.Sort(SocialAssertionList(h.UnresolvedWriters))
	sort.Sort(SocialAssertionList(h.UnresolvedReaders))
	return h
}

// Extensions returns a list of extensions for the given handle.
func (h Handle) Extensions() (extensions []HandleExtension) {
	if h.ConflictInfo != nil {
		extensions = append(extensions, *h.ConflictInfo)
	}
	if h.FinalizedInfo != nil {
		extensions = append(extensions, *h.FinalizedInfo)
	}
	return extensions
}

// IsFinal returns true if the handle has been finalized.
func (h Handle) IsFinal() bool {
	return h.FinalizedInfo != nil
}

// IsConflict returns true if the handle is a conflict handle.
func (h Handle) IsConflict() bool {
	return h.ConflictInfo != nil
}

// DeepEqual returns true if the handle is equal to another handle.
func (h Handle) DeepEqual(other Handle) bool {
	if !h.sorted {
		sort.Sort(UIDList(h.Writers))
		sort.Sort(UIDList(h.Readers))
		sort.Sort(SocialAssertionList(h.UnresolvedWriters))
		sort.Sort(SocialAssertionList(h.UnresolvedReaders))
		h.sorted = true
	}
	if !other.sorted {
		sort.Sort(UIDList(other.Writers))
		sort.Sort(UIDList(other.Readers))
		sort.Sort(SocialAssertionList(other.UnresolvedWriters))
		sort.Sort(SocialAssertionList(other.UnresolvedReaders))
		other.sorted = true
	}

	if len(h.Writers) != len(other.Writers) {
		return false
	}
	if len(h.UnresolvedWriters) != len(other.UnresolvedWriters) {
		return false
	}
	if len(h.Readers) != len(other.Readers) {
		return false
	}
	if len(h.UnresolvedReaders) != len(other.UnresolvedReaders) {
		return false
	}
	for i, v := range h.Writers {
		if other.Writers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedWriters {
		if other.UnresolvedWriters[i] != v {
			return false
		}
	}
	for i, v := range h.Readers {
		if other.Readers[i] != v {
			return false
		}
	}
	for i, v := range h.UnresolvedReaders {
		if other.UnresolvedReaders[i] != v {
			return false
		}
	}
	if h.IsConflict() != other.IsConflict() {
		return false
	}
	if h.IsFinal() != other.IsFinal() {
		return false
	}
	if h.ConflictInfo != nil &&
		h.ConflictInfo.String() != other.ConflictInfo.String() {
		return false
	}
	if h.FinalizedInfo != nil &&
		h.FinalizedInfo.String() != other.FinalizedInfo.String() {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package wiki

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"regexp"
	"text/template"
	"time"

	"errors"

	"database/sql"

	"github.com/mahata/gwiki/util"
	_ "github.com/mattn/go-sqlite3"
	"github.com/russross/blackfriday"
)

type Config struct {
	TxtDir   string `json:"txtDir"`
	ImgDir   string `json:"imgDir`
	Password string `json:"password`
}

var templates = template.Must(template.ParseFiles("login.html", "edit.html", "view.html", "upload-file.html"))

var validPath = regexp.MustCompile(`^/(edit|save|view|static)/(\S+)$`)
var config Config

type Page struct {
	Title   string
	Content []byte
	Login   bool
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

func isExist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func toHash(password string) string {
	converted := sha256.Sum256([]byte(password))
	return hex.EncodeToString(converted[:])
}

func (p *Page) saveSqlite() error {
	// FixMe: WIP
	db, err := sql.Open("sqlite3", "./sample.sqlite3")
	checkErr(err)
	stmt, err := db.Prepare("INSERT OR REPLACE INTO wiki (title, content, unixtime) values(?, ?, ?)")
	checkErr(err)
	res, err := stmt.Exec(p.Title, p.Content, string(fmt.Sprint(time.Now().Unix())))
	checkErr(err)
	id, err := res.LastInsertId()
	checkErr(err)
	fmt.Println(id)

	return err
}

func (p *Page) save() error {
	archiveDir := config.TxtDir + "/" + p.Title
	if !isExist(archiveDir) {
		err := os.Mkdir(archiveDir, 0700)
		if err != nil {
			os.Stderr.WriteString("Failed to create a directory. Is your disk full?")
			panic(err)
		}
	}
	archiveFilePath := archiveDir + "/" + string(fmt.Sprint(time.Now().Unix()))
	archiveErr := ioutil.WriteFile(archiveFilePath, p.Content, 0600)
	if archiveErr != nil {
		return archiveErr
	}
	filePath := archiveDir + ".txt"

	return ioutil.WriteFile(filePath, p.Content, 0600)
}

func loadConf(confFile string) {
	file, err := ioutil.ReadFile(confFile)
	if err != nil {
		panic(err)
	}

	json.Unmarshal(file, &config)
}

func loadPage(title string) (*Page, error) {
	filename := config.TxtDir + "/" + title + ".txt"
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	//db, err := sql.Open("sqlite3", "./sample.sqlite3")
	//checkErr(err)
	//
	//rows, err := db.Query("SELECT * FROM wiki")
	//checkErr(err)
	//for rows.Next() {
	//	var id int
	//	var title string
	//	var content string
	//	var unixtime int
	//	err = rows.Scan(&id, &title, &content, &unixtime)
	//	fmt.Println(id, title, content, unixtime)
	//}

	return &Page{Title: title, Content: content}, nil
}

func Run(useNginx bool) {
	loadConf("config.json")

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/upload-file", uploadFileHandler)
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.HandleFunc("/static/", makeHandler(staticHandler))

	if useNginx {
		listen, err := net.Listen("tcp", "0.0.0.0:8080")
		if err != nil {
			os.Stderr.WriteString("Failed to copy the HTTP server. Is port 8080 available?")
			panic(err)
		}
		fcgi.Serve(listen, nil)
	} else {
		err := http.ListenAndServe(":8080", nil)
		if err != nil {
			os.Stderr.WriteString("Failed to copy the HTTP server. Is port 8080 available?")
			panic(err)
		}
	}
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "/view/index", http.StatusFound)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		p := &Page{
			Title:   "Login Page",
			Content: []byte("Login:"),
		}
		renderTemplate(w, "login", p)
	} else {
		if r.PostFormValue("password") == config.Password {
			cookie := http.Cookie{
				Name:     "login",
				Value:    toHash(config.Password),
				HttpOnly: true,
				Expires:  time.Now().AddDate(0, 1, 0),
			}
			http.SetCookie(w, &cookie)
			http.Redirect(w, r, "/", http.StatusFound)
		} else {
			http.Error(w, "Login password doesn't match", http.StatusForbidden)
		}
	}
}

func calcExtension(contentType string) (string, error) {
	switch contentType {
	case "image/jpeg":
		return ".jpg", nil
	case "image/gif":
		return ".gif", nil
	case "image/png":
		return ".png", nil
	default:
		return "", errors.New("Only jpeg, gif and png files are allowed for the file upload")
	}
}

func uploadFileHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		http.Error(w, "Upload data have to be passed with POST.", http.StatusBadRequest)
	} else {
		r.ParseMultipartForm(32 << 20) // maxMemory
		file, handler, err := r.FormFile("upload-file")
		if err != nil {
			http.Error(w, "upload-file parameter must be passed", http.StatusBadRequest)
			return
		}
		defer file.Close()

		extension, err := calcExtension(handler.Header.Get(("Content-Type")))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		uploadFileName := util.GetRandomString() + extension
		f, err := os.OpenFile(fmt.Sprintf("%s/%s", config.ImgDir, uploadFileName), os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			http.Error(w, "Can't create a file handler. Disk full?", http.StatusInternalServerError)
			return
		}
		defer f.Close()

		_, err = io.Copy(f, file)
		if err != nil {
			http.Error(w, "Can't upload the upload-file. Disk full?", http.StatusInternalServerError)
			return
		}

		p := &Page{
			Title:   "File Posted",
			Content: []byte(fmt.Sprintf("![AltText](/static/%s \"TitleText\")", uploadFileName)),
		}
		renderTemplate(w, "upload-file", p)
	}
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}

	cookie, err := r.Cookie("login")
	if err != nil {
		p.Login = false
	} else {
		p.Login = cookie.Value == toHash(config.Password)
	}

	p.Content = blackfriday.MarkdownCommon([]byte(p.Content))
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadPage(title)
	if err != nil {
		// Looks like there's no page to edit
		ioutil.WriteFile(fmt.Sprintf("%s/%s.txt", config.TxtDir, title), []byte(""), 0600)
		p, _ = loadPage(title)
	}

	cookie, err := r.Cookie("login")
	if err != nil {
		p.Login = false
	} else {
		p.Login = cookie.Value == toHash(config.Password)
	}

	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	content := r.FormValue("content")
	p := &Page{Title: title, Content: []byte(content)}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func staticHandler(w http.ResponseWriter, r *http.Request, fpath string) {
	imgPath := config.ImgDir + "/" + fpath
	_, err := os.Stat(imgPath)
	if err == nil {
		http.ServeFile(w, r, imgPath)
	} else {
		http.ServeFile(w, r, config.ImgDir+"/not-found.png") // FixMe: Add this image when installing the app
	}
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}
<commit_msg>Working... anyway<commit_after>package wiki

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"regexp"
	"text/template"
	"time"

	"errors"

	"database/sql"

	"github.com/mahata/gwiki/util"
	_ "github.com/mattn/go-sqlite3"
	"github.com/russross/blackfriday"
)

type Config struct {
	TxtDir   string `json:"txtDir"`
	ImgDir   string `json:"imgDir`
	Password string `json:"password`
}

var templates = template.Must(template.ParseFiles("login.html", "edit.html", "view.html", "upload-file.html"))

var validPath = regexp.MustCompile(`^/(edit|save|view|static)/(\S+)$`)
var config Config

type Page struct {
	Title   string
	Content []byte
	Login   bool
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

func isExist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func toHash(password string) string {
	converted := sha256.Sum256([]byte(password))
	return hex.EncodeToString(converted[:])
}

func (p *Page) saveSqlite() error {
	// FixMe: WIP
	db, err := sql.Open("sqlite3", "./sample.sqlite3")
	checkErr(err)
	stmt, err := db.Prepare("INSERT OR REPLACE INTO wiki (title, content, unixtime) values(?, ?, ?)")
	checkErr(err)
	res, err := stmt.Exec(p.Title, p.Content, string(fmt.Sprint(time.Now().Unix())))
	checkErr(err)
	id, err := res.LastInsertId()
	checkErr(err)
	fmt.Println(id)

	return err
}

func loadSqlite(title string) (*Page, error) {
	db, err := sql.Open("sqlite3", "./sample.sqlite3")
	if err != nil {
		return nil, err
	}
	defer db.Close()

	stmt, err := db.Prepare("SELECT * FROM wiki where title = ?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	var id, unixtime int
	var _title, content string
	err = stmt.QueryRow(title).Scan(&id, &_title, &content, &unixtime)
	if err != nil {
		return nil, err
	}

	return &Page{Title: title, Content: []byte(content)}, nil
}

//func (p *Page) save() error {
//	archiveDir := config.TxtDir + "/" + p.Title
//	if !isExist(archiveDir) {
//		err := os.Mkdir(archiveDir, 0700)
//		if err != nil {
//			os.Stderr.WriteString("Failed to create a directory. Is your disk full?")
//			panic(err)
//		}
//	}
//	archiveFilePath := archiveDir + "/" + string(fmt.Sprint(time.Now().Unix()))
//	archiveErr := ioutil.WriteFile(archiveFilePath, p.Content, 0600)
//	if archiveErr != nil {
//		return archiveErr
//	}
//	filePath := archiveDir + ".txt"
//
//	return ioutil.WriteFile(filePath, p.Content, 0600)
//}

func loadConf(confFile string) {
	file, err := ioutil.ReadFile(confFile)
	if err != nil {
		panic(err)
	}

	json.Unmarshal(file, &config)
}

//func loadPage(title string) (*Page, error) {
//	filename := config.TxtDir + "/" + title + ".txt"
//	content, err := ioutil.ReadFile(filename)
//	if err != nil {
//		return nil, err
//	}
//
//	return &Page{Title: title, Content: content}, nil
//}

func Run(useNginx bool) {
	loadConf("config.json")

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/upload-file", uploadFileHandler)
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.HandleFunc("/static/", makeHandler(staticHandler))

	if useNginx {
		listen, err := net.Listen("tcp", "0.0.0.0:8080")
		if err != nil {
			os.Stderr.WriteString("Failed to copy the HTTP server. Is port 8080 available?")
			panic(err)
		}
		fcgi.Serve(listen, nil)
	} else {
		err := http.ListenAndServe(":8080", nil)
		if err != nil {
			os.Stderr.WriteString("Failed to copy the HTTP server. Is port 8080 available?")
			panic(err)
		}
	}
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "/view/index", http.StatusFound)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		p := &Page{
			Title:   "Login Page",
			Content: []byte("Login:"),
		}
		renderTemplate(w, "login", p)
	} else {
		if r.PostFormValue("password") == config.Password {
			cookie := http.Cookie{
				Name:     "login",
				Value:    toHash(config.Password),
				HttpOnly: true,
				Expires:  time.Now().AddDate(0, 1, 0),
			}
			http.SetCookie(w, &cookie)
			http.Redirect(w, r, "/", http.StatusFound)
		} else {
			http.Error(w, "Login password doesn't match", http.StatusForbidden)
		}
	}
}

func calcExtension(contentType string) (string, error) {
	switch contentType {
	case "image/jpeg":
		return ".jpg", nil
	case "image/gif":
		return ".gif", nil
	case "image/png":
		return ".png", nil
	default:
		return "", errors.New("Only jpeg, gif and png files are allowed for the file upload")
	}
}

func uploadFileHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		http.Error(w, "Upload data have to be passed with POST.", http.StatusBadRequest)
	} else {
		r.ParseMultipartForm(32 << 20) // maxMemory
		file, handler, err := r.FormFile("upload-file")
		if err != nil {
			http.Error(w, "upload-file parameter must be passed", http.StatusBadRequest)
			return
		}
		defer file.Close()

		extension, err := calcExtension(handler.Header.Get(("Content-Type")))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		uploadFileName := util.GetRandomString() + extension
		f, err := os.OpenFile(fmt.Sprintf("%s/%s", config.ImgDir, uploadFileName), os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			http.Error(w, "Can't create a file handler. Disk full?", http.StatusInternalServerError)
			return
		}
		defer f.Close()

		_, err = io.Copy(f, file)
		if err != nil {
			http.Error(w, "Can't upload the upload-file. Disk full?", http.StatusInternalServerError)
			return
		}

		p := &Page{
			Title:   "File Posted",
			Content: []byte(fmt.Sprintf("![AltText](/static/%s \"TitleText\")", uploadFileName)),
		}
		renderTemplate(w, "upload-file", p)
	}
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	// p, err := loadPage(title)
	p, err := loadSqlite(title)
	if err != nil {
		fmt.Println(err)
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}

	cookie, err := r.Cookie("login")
	if err != nil {
		p.Login = false
	} else {
		p.Login = cookie.Value == toHash(config.Password)
	}

	p.Content = blackfriday.MarkdownCommon([]byte(p.Content))
	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, title string) {
	p, err := loadSqlite(title)
	if err != nil {
		p := &Page{Title: title, Content: []byte("Edit Me!"), Login: false}
		p.saveSqlite()

		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
	}

	cookie, err := r.Cookie("login")
	if err != nil {
		p.Login = false
	} else {
		fmt.Println(&p.Login)
		p.Login = cookie.Value == toHash(config.Password)
	}

	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
	content := r.FormValue("content")
	p := &Page{Title: title, Content: []byte(content)}

	// This block will be deleted soon
	//err := p.save()
	//if err != nil {
	//	http.Error(w, err.Error(), http.StatusInternalServerError)
	//	return
	//}

	// This block will replace the previous one
	err := p.saveSqlite()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, "/view/"+title, http.StatusFound)
}

func staticHandler(w http.ResponseWriter, r *http.Request, fpath string) {
	imgPath := config.ImgDir + "/" + fpath
	_, err := os.Stat(imgPath)
	if err == nil {
		http.ServeFile(w, r, imgPath)
	} else {
		http.ServeFile(w, r, config.ImgDir+"/not-found.png") // FixMe: Add this image when installing the app
	}
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}
<|endoftext|>


<commit_before>package dhcp4client

import (
	"crypto/rand"
	"encoding/binary"
	"net"
	"time"

	"golang.org/x/sys/unix"
)

const (
	minIPHdrLen = 20
	maxIPHdrLen = 60
	udpHdrLen   = 8
	ip4Ver      = 0x40
	ttl         = 16
	srcPort     = 68
	dstPort     = 67
)

var (
	bcastMAC = []byte{255, 255, 255, 255, 255, 255}
)

// abstracts AF_PACKET
type packetSock struct {
	fd      int
	ifindex int
}

func NewPacketSock(ifindex int) (*packetSock, error) {
	fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(swap16(unix.ETH_P_IP)))
	if err != nil {
		return nil, err
	}

	addr := unix.SockaddrLinklayer{
		Ifindex:  ifindex,
		Protocol: swap16(unix.ETH_P_IP),
	}

	if err = unix.Bind(fd, &addr); err != nil {
		return nil, err
	}

	return &packetSock{
		fd:      fd,
		ifindex: ifindex,
	}, nil
}

func (pc *packetSock) Close() error {
	return unix.Close(pc.fd)
}

func (pc *packetSock) Write(packet []byte) error {
	lladdr := unix.SockaddrLinklayer{
		Ifindex:  pc.ifindex,
		Protocol: swap16(unix.ETH_P_IP),
		Halen:    uint8(len(bcastMAC)),
	}
	copy(lladdr.Addr[:], bcastMAC)

	pkt := make([]byte, minIPHdrLen+udpHdrLen+len(packet))

	fillIPHdr(pkt[0:minIPHdrLen], udpHdrLen+uint16(len(packet)))
	fillUDPHdr(pkt[minIPHdrLen:minIPHdrLen+udpHdrLen], uint16(len(packet)))

	// payload
	copy(pkt[minIPHdrLen+udpHdrLen:len(pkt)], packet)

	return unix.Sendto(pc.fd, pkt, 0, &lladdr)
}

func (pc *packetSock) ReadFrom() ([]byte, net.IP, error) {
	pkt := make([]byte, maxIPHdrLen+udpHdrLen+MaxDHCPLen)
	n, _, err := unix.Recvfrom(pc.fd, pkt, 0)
	if err != nil {
		return nil, nil, err
	}

	// IP hdr len
	ihl := int(pkt[0]&0x0F) * 4
	// Source IP address
	src := net.IP(pkt[12:16])

	return pkt[ihl+udpHdrLen : n], src, nil
}

func (pc *packetSock) SetReadTimeout(t time.Duration) error {

	tv := unix.NsecToTimeval(t.Nanoseconds())
	return unix.SetsockoptTimeval(pc.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv)
}

// compute's 1's complement checksum
func chksum(p []byte, csum []byte) {
	cklen := len(p)
	s := uint32(0)
	for i := 0; i < (cklen - 1); i += 2 {
		s += uint32(p[i+1])<<8 | uint32(p[i])
	}
	if cklen&1 == 1 {
		s += uint32(p[cklen-1])
	}
	s = (s >> 16) + (s & 0xffff)
	s = s + (s >> 16)
	s = ^s

	csum[0] = uint8(s & 0xff)
	csum[1] = uint8(s >> 8)
}

func fillIPHdr(hdr []byte, payloadLen uint16) {
	// version + IHL
	hdr[0] = ip4Ver | (minIPHdrLen / 4)
	// total length
	binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr))+payloadLen)
	// identification
	if _, err := rand.Read(hdr[4:5]); err != nil {
		panic(err)
	}
	// TTL
	hdr[8] = 16
	// Protocol
	hdr[9] = unix.IPPROTO_UDP
	// dst IP
	copy(hdr[16:20], net.IPv4bcast.To4())
	// compute IP hdr checksum
	chksum(hdr[0:len(hdr)], hdr[10:12])
}

func fillUDPHdr(hdr []byte, payloadLen uint16) {
	// src port
	binary.BigEndian.PutUint16(hdr[0:2], srcPort)
	// dest port
	binary.BigEndian.PutUint16(hdr[2:4], dstPort)
	// length
	binary.BigEndian.PutUint16(hdr[4:6], udpHdrLen+payloadLen)
}

func swap16(x uint16) uint16 {
	var b [2]byte
	binary.BigEndian.PutUint16(b[:], x)
	return binary.LittleEndian.Uint16(b[:])
}
<commit_msg>pktsock: use math.Rand over crypto.Rand<commit_after>package dhcp4client

import (
	"encoding/binary"
	"math/rand"
	"net"
	"time"

	"golang.org/x/sys/unix"
)

const (
	minIPHdrLen = 20
	maxIPHdrLen = 60
	udpHdrLen   = 8
	ip4Ver      = 0x40
	ttl         = 16
	srcPort     = 68
	dstPort     = 67
)

var (
	bcastMAC = []byte{255, 255, 255, 255, 255, 255}
)

// abstracts AF_PACKET
type packetSock struct {
	fd      int
	ifindex int
}

func NewPacketSock(ifindex int) (*packetSock, error) {
	fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, int(swap16(unix.ETH_P_IP)))
	if err != nil {
		return nil, err
	}

	addr := unix.SockaddrLinklayer{
		Ifindex:  ifindex,
		Protocol: swap16(unix.ETH_P_IP),
	}

	if err = unix.Bind(fd, &addr); err != nil {
		return nil, err
	}

	return &packetSock{
		fd:      fd,
		ifindex: ifindex,
	}, nil
}

func (pc *packetSock) Close() error {
	return unix.Close(pc.fd)
}

func (pc *packetSock) Write(packet []byte) error {
	lladdr := unix.SockaddrLinklayer{
		Ifindex:  pc.ifindex,
		Protocol: swap16(unix.ETH_P_IP),
		Halen:    uint8(len(bcastMAC)),
	}
	copy(lladdr.Addr[:], bcastMAC)

	pkt := make([]byte, minIPHdrLen+udpHdrLen+len(packet))

	fillIPHdr(pkt[0:minIPHdrLen], udpHdrLen+uint16(len(packet)))
	fillUDPHdr(pkt[minIPHdrLen:minIPHdrLen+udpHdrLen], uint16(len(packet)))

	// payload
	copy(pkt[minIPHdrLen+udpHdrLen:len(pkt)], packet)

	return unix.Sendto(pc.fd, pkt, 0, &lladdr)
}

func (pc *packetSock) ReadFrom() ([]byte, net.IP, error) {
	pkt := make([]byte, maxIPHdrLen+udpHdrLen+MaxDHCPLen)
	n, _, err := unix.Recvfrom(pc.fd, pkt, 0)
	if err != nil {
		return nil, nil, err
	}

	// IP hdr len
	ihl := int(pkt[0]&0x0F) * 4
	// Source IP address
	src := net.IP(pkt[12:16])

	return pkt[ihl+udpHdrLen : n], src, nil
}

func (pc *packetSock) SetReadTimeout(t time.Duration) error {

	tv := unix.NsecToTimeval(t.Nanoseconds())
	return unix.SetsockoptTimeval(pc.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv)
}

// compute's 1's complement checksum
func chksum(p []byte, csum []byte) {
	cklen := len(p)
	s := uint32(0)
	for i := 0; i < (cklen - 1); i += 2 {
		s += uint32(p[i+1])<<8 | uint32(p[i])
	}
	if cklen&1 == 1 {
		s += uint32(p[cklen-1])
	}
	s = (s >> 16) + (s & 0xffff)
	s = s + (s >> 16)
	s = ^s

	csum[0] = uint8(s & 0xff)
	csum[1] = uint8(s >> 8)
}

func fillIPHdr(hdr []byte, payloadLen uint16) {
	// version + IHL
	hdr[0] = ip4Ver | (minIPHdrLen / 4)
	// total length
	binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr))+payloadLen)
	// identification
	if _, err := rand.Read(hdr[4:5]); err != nil {
		panic(err)
	}
	// TTL
	hdr[8] = 16
	// Protocol
	hdr[9] = unix.IPPROTO_UDP
	// dst IP
	copy(hdr[16:20], net.IPv4bcast.To4())
	// compute IP hdr checksum
	chksum(hdr[0:len(hdr)], hdr[10:12])
}

func fillUDPHdr(hdr []byte, payloadLen uint16) {
	// src port
	binary.BigEndian.PutUint16(hdr[0:2], srcPort)
	// dest port
	binary.BigEndian.PutUint16(hdr[2:4], dstPort)
	// length
	binary.BigEndian.PutUint16(hdr[4:6], udpHdrLen+payloadLen)
}

func swap16(x uint16) uint16 {
	var b [2]byte
	binary.BigEndian.PutUint16(b[:], x)
	return binary.LittleEndian.Uint16(b[:])
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
)

func StartServer(config *SiteConfig) {
	gb := NewGoblin(config)
	err := gb.Init()
	if err != nil {
		log.Errorf("Goblin init fail. %s", err)
		return
	}
	gb.StartServer()
}

func main() {
	configPtr := flag.String("c", "config.toml", "Config file path.")
	flag.Parse()
	if len(*configPtr) < 1 {
		log.Error("Config file path must set.Use -h to get some help.")
	}

	var config SiteConfig
	if _, err := toml.DecodeFile(*configPtr, &config); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%#v \n", config)
	StartServer(&config)
}
<commit_msg>Add dbconnection to env support.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
	"os"
)

func StartServer(config *SiteConfig) {
	gb := NewGoblin(config)
	err := gb.Init()
	if err != nil {
		log.Errorf("Goblin init fail. %s", err)
		return
	}
	gb.StartServer()
}

func main() {
	configPtr := flag.String("c", "config.toml", "Config file path.")
	flag.Parse()
	if len(*configPtr) < 1 {
		log.Error("Config file path must set.Use -h to get some help.")
	}

	var config SiteConfig
	if _, err := toml.DecodeFile(*configPtr, &config); err != nil {
		fmt.Println(err)
		return
	}

	dbUrl := os.Getenv("DATABASE_URL")
	if dbUrl != "" {
		config.DBConnection = dbUrl
	}

	fmt.Printf("%#v \n", config)
	StartServer(&config)
}
<|endoftext|>


<commit_before>package docker

import (
	"container/list"
	"strings"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"golang.org/x/net/context"
)

// GetAllContainer is get all containers info from docker engine.
func GetAllContainer() *list.List {
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	cli, err := client.NewClient("http://localhost:2375", "v1.21", nil, defaultHeaders)
	if err != nil {
		panic(err)
	}
	options := types.ContainerListOptions{All: true}
	containers, err := cli.ContainerList(context.Background(), options)
	if err != nil {
		panic(err)
	}
	containerList := list.New()
	for _, c := range containers {
		container := make(map[string]string)
		container["id"] = c.ID
		container["image"] = c.Image
		container["status"] = c.Status
		container["command"] = c.Command
		container["name"] = strings.Join(c.Names[:], ",")
		containerList.PushBack(container)
	}
	return containerList
}
<commit_msg>1. clean code 2. add websocket feature<commit_after><|endoftext|>


<commit_before>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 cmd

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/elastic/go-elasticsearch/v8/internal/build/cmd"
	"github.com/elastic/go-elasticsearch/v8/internal/build/utils"
	"github.com/elastic/go-elasticsearch/v8/internal/version"
	"github.com/spf13/cobra"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var (
	output     *string
	commitHash *string
	debug      *bool
	info       *bool
)

func init() {
	output = toolsCmd.Flags().StringP("output", "o", "", "Path to a folder for generated output")
	commitHash = toolsCmd.Flags().StringP("commit_hash", "c", "", "Elasticsearch commit hash")
	debug = toolsCmd.Flags().BoolP("debug", "d", false, "Print the generated source to terminal")
	info = toolsCmd.Flags().Bool("info", false, "Print the API details to terminal")

	cmd.RegisterCmd(toolsCmd)
}

var toolsCmd = &cobra.Command{
	Use:   "download-spec",
	Short: "Downdload specification artifact for code & tests generation",
	Run: func(cmd *cobra.Command, args []string) {
		command := &Command{
			Output:     *output,
			CommitHash: *commitHash,
			Debug:      *debug,
			Info:       *info,
		}
		err := command.Execute()
		if err != nil {
			utils.PrintErr(err)
			os.Exit(1)
		}
	},
}

type Command struct {
	Output     string
	CommitHash string
	Debug      bool
	Info       bool
}

// download-spec runs a query to the Elastic artifact API, retrieve the list of active artifacts
// downloads, extract and write to disk the rest-resources spec alongside a json with relevant build information.
func (c Command) Execute() (err error) {
	const artifactsUrl = "https://artifacts-api.elastic.co/v1/versions"

	esBuildVersion := os.Getenv("ELASTICSEARCH_BUILD_VERSION")
	if esBuildVersion == "" {
		esBuildVersion = version.Client
	}

	versionUrl := strings.Join([]string{artifactsUrl, esBuildVersion}, "/")

	res, err := http.Get(versionUrl)
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer res.Body.Close()

	var v Versions
	dec := json.NewDecoder(res.Body)
	err = dec.Decode(&v)
	if err != nil {
		log.Fatalf(err.Error())
	}

	if c.Debug {
		log.Printf("%d builds found", len(v.Version.Builds))
	}

	var build Build
	if c.CommitHash != "" {
		if build, err = findBuildByCommitHash(c.CommitHash, v.Version.Builds); err != nil {
			build = findMostRecentBuild(v.Version.Builds)
		}
	} else {
		build = findMostRecentBuild(v.Version.Builds)
	}
	if c.Debug {
		log.Printf("Build found : %s", build.Projects.Elasticsearch.CommitHash)
	}

	data, err := c.downloadZip(build)
	if err != nil {
		log.Fatalf("Cannot download zip from %s, reason : %s", build.zipfileUrl(), err)
	}

	if err := c.extractZipToDest(data); err != nil {
		log.Fatalf(err.Error())
	}

	d, _ := json.Marshal(build)

	err = c.writeFileToDest("elasticsearch.json", d)
	if err != nil {
		log.Fatalf(err.Error())
	}

	return nil
}

func (c Command) writeFileToDest(filename string, data []byte) error {
	path := filepath.Join(c.Output, filename)
	if err := ioutil.WriteFile(path, data, 0644); err != nil {
		return fmt.Errorf("cannot write file: %s", err)
	}
	if c.Debug {
		log.Printf("Successfuly written file to : %s", path)
	}
	return nil
}

type Versions struct {
	Version struct {
		Builds []Build `json:"builds"`
	} `json:"version"`
}

type PackageUrl struct {
	*url.URL
}

func (p *PackageUrl) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}
	url, err := url.Parse(string(data[1 : len(data)-1]))
	if err == nil {
		p.URL = url
	}
	return err
}

type BuildStartTime struct {
	*time.Time
}

func (t *BuildStartTime) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}
	var err error
	parsedTime, err := time.Parse(`"`+"Mon, 2 Jan 2006 15:04:05 MST"+`"`, string(data))
	t.Time = &parsedTime
	return err
}

type Build struct {
	StartTime BuildStartTime `json:"start_time"`
	Version   string         `json:"version"`
	BuildId   string         `json:"build_id"`
	Projects  struct {
		Elasticsearch struct {
			Branch     string `json:"branch"`
			CommitHash string `json:"commit_hash"`
			Packages   map[string]struct {
				Url  PackageUrl `json:"url"`
				Type string     `json:"type"`
			}
		} `json:"elasticsearch"`
	} `json:"projects"`
}

func NewBuild() Build {
	t := time.Date(1970, 0,0,0,0,0,0, time.UTC)
	startTime := BuildStartTime{Time: &t}
	return Build{StartTime: startTime}
}

// zipfileUrl return the file URL for the rest-resources artifact from Build
// There should be only one artifact matching the requirements par Build.
func (b Build) zipfileUrl() string {
	for _, pack := range b.Projects.Elasticsearch.Packages {
		if pack.Type == "zip" && strings.Contains(pack.Url.String(), "rest-resources") {
			return pack.Url.String()
		}
	}
	return ""
}

// extractZipToDest extract the data from a previously downloaded file loaded in memory to Output target.
func (c Command) extractZipToDest(data []byte) error {
	zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
	if err != nil {
		return err
	}

	if err = os.MkdirAll(c.Output, 0744); err != nil {
		return fmt.Errorf("cannot created destination directory: %s", err)
	}

	for _, file := range zipReader.File {
		f, err := file.Open()
		if err != nil {
			return fmt.Errorf("cannot read file in zipfile: %s", err)
		}

		if file.FileInfo().IsDir() {
			path := filepath.Join(c.Output, file.Name)
			_ = os.MkdirAll(path, 0744)
		} else {
			data, err := ioutil.ReadAll(f)
			if err != nil {
				return err
			}

			if err := c.writeFileToDest(file.Name, data); err != nil {
				return err
			}
		}
	}

	if c.Debug {
		log.Printf("Zipfile successfully extracted to %s", c.Output)
	}

	return nil
}

// downloadZip fetches the rest-resources artifact from a Build and return its content as []byte.
func (c Command) downloadZip(b Build) ([]byte, error) {
	url := b.zipfileUrl()
	if c.Debug {
		log.Printf("Zipfile url : %s", b.zipfileUrl())
	}

	client := &http.Client{}

	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Accept-Content", "gzip")
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	data, _ := ioutil.ReadAll(res.Body)
	return data, err
}

// findMostRecentBuild iterates through the builds retrieved from the api
// and return the latest one based on the StartTime of each Build.
func findMostRecentBuild(builds []Build) Build {
	var latestBuild Build
	latestBuild = NewBuild()
	for _, build := range builds {
		if build.StartTime.After(*latestBuild.StartTime.Time) {
			latestBuild = build
		}
	}
	return latestBuild
}

// findBuildByCommitHash iterates through the builds and returns the first occurrence of Build
// that matches the provided commitHash.
func findBuildByCommitHash(commitHash string, builds []Build) (Build, error) {
	for _, build := range builds {
		if build.Projects.Elasticsearch.CommitHash == commitHash {
			return build, nil
		}
	}
	return Build{}, fmt.Errorf("Build with commit hash %s not found", commitHash)
}
<commit_msg>fix: close open file (#320)<commit_after>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 cmd

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/elastic/go-elasticsearch/v8/internal/build/cmd"
	"github.com/elastic/go-elasticsearch/v8/internal/build/utils"
	"github.com/elastic/go-elasticsearch/v8/internal/version"
	"github.com/spf13/cobra"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var (
	output     *string
	commitHash *string
	debug      *bool
	info       *bool
)

func init() {
	output = toolsCmd.Flags().StringP("output", "o", "", "Path to a folder for generated output")
	commitHash = toolsCmd.Flags().StringP("commit_hash", "c", "", "Elasticsearch commit hash")
	debug = toolsCmd.Flags().BoolP("debug", "d", false, "Print the generated source to terminal")
	info = toolsCmd.Flags().Bool("info", false, "Print the API details to terminal")

	cmd.RegisterCmd(toolsCmd)
}

var toolsCmd = &cobra.Command{
	Use:   "download-spec",
	Short: "Download specification artifact for code & tests generation",
	Run: func(cmd *cobra.Command, args []string) {
		command := &Command{
			Output:     *output,
			CommitHash: *commitHash,
			Debug:      *debug,
			Info:       *info,
		}
		err := command.Execute()
		if err != nil {
			utils.PrintErr(err)
			os.Exit(1)
		}
	},
}

type Command struct {
	Output     string
	CommitHash string
	Debug      bool
	Info       bool
}

// download-spec runs a query to the Elastic artifact API, retrieve the list of active artifacts
// downloads, extract and write to disk the rest-resources spec alongside a json with relevant build information.
func (c Command) Execute() (err error) {
	const artifactsUrl = "https://artifacts-api.elastic.co/v1/versions"

	esBuildVersion := os.Getenv("ELASTICSEARCH_BUILD_VERSION")
	if esBuildVersion == "" {
		esBuildVersion = version.Client
	}

	versionUrl := strings.Join([]string{artifactsUrl, esBuildVersion}, "/")

	res, err := http.Get(versionUrl)
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer res.Body.Close()

	var v Versions
	dec := json.NewDecoder(res.Body)
	err = dec.Decode(&v)
	if err != nil {
		log.Fatalf(err.Error())
	}

	if c.Debug {
		log.Printf("%d builds found", len(v.Version.Builds))
	}

	var build Build
	if c.CommitHash != "" {
		if build, err = findBuildByCommitHash(c.CommitHash, v.Version.Builds); err != nil {
			build = findMostRecentBuild(v.Version.Builds)
		}
	} else {
		build = findMostRecentBuild(v.Version.Builds)
	}
	if c.Debug {
		log.Printf("Build found : %s", build.Projects.Elasticsearch.CommitHash)
	}

	data, err := c.downloadZip(build)
	if err != nil {
		log.Fatalf("Cannot download zip from %s, reason : %s", build.zipfileUrl(), err)
	}

	if err := c.extractZipToDest(data); err != nil {
		log.Fatalf(err.Error())
	}

	d, _ := json.Marshal(build)

	err = c.writeFileToDest("elasticsearch.json", d)
	if err != nil {
		log.Fatalf(err.Error())
	}

	return nil
}

func (c Command) writeFileToDest(filename string, data []byte) error {
	path := filepath.Join(c.Output, filename)
	if err := ioutil.WriteFile(path, data, 0644); err != nil {
		return fmt.Errorf("cannot write file: %s", err)
	}
	if c.Debug {
		log.Printf("Successfuly written file to : %s", path)
	}
	return nil
}

type Versions struct {
	Version struct {
		Builds []Build `json:"builds"`
	} `json:"version"`
}

type PackageUrl struct {
	*url.URL
}

func (p *PackageUrl) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}
	url, err := url.Parse(string(data[1 : len(data)-1]))
	if err == nil {
		p.URL = url
	}
	return err
}

type BuildStartTime struct {
	*time.Time
}

func (t *BuildStartTime) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}
	var err error
	parsedTime, err := time.Parse(`"`+"Mon, 2 Jan 2006 15:04:05 MST"+`"`, string(data))
	t.Time = &parsedTime
	return err
}

type Build struct {
	StartTime BuildStartTime `json:"start_time"`
	Version   string         `json:"version"`
	BuildId   string         `json:"build_id"`
	Projects  struct {
		Elasticsearch struct {
			Branch     string `json:"branch"`
			CommitHash string `json:"commit_hash"`
			Packages   map[string]struct {
				Url  PackageUrl `json:"url"`
				Type string     `json:"type"`
			}
		} `json:"elasticsearch"`
	} `json:"projects"`
}

func NewBuild() Build {
	t := time.Date(1970, 0,0,0,0,0,0, time.UTC)
	startTime := BuildStartTime{Time: &t}
	return Build{StartTime: startTime}
}

// zipfileUrl return the file URL for the rest-resources artifact from Build
// There should be only one artifact matching the requirements par Build.
func (b Build) zipfileUrl() string {
	for _, pack := range b.Projects.Elasticsearch.Packages {
		if pack.Type == "zip" && strings.Contains(pack.Url.String(), "rest-resources") {
			return pack.Url.String()
		}
	}
	return ""
}

// extractZipToDest extract the data from a previously downloaded file loaded in memory to Output target.
func (c Command) extractZipToDest(data []byte) error {
	zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
	if err != nil {
		return err
	}

	if err = os.MkdirAll(c.Output, 0744); err != nil {
		return fmt.Errorf("cannot created destination directory: %s", err)
	}

	for _, file := range zipReader.File {
		f, err := file.Open()
		if err != nil {
			return fmt.Errorf("cannot read file in zipfile: %s", err)
		}
		defer f.Close()

		if file.FileInfo().IsDir() {
			path := filepath.Join(c.Output, file.Name)
			_ = os.MkdirAll(path, 0744)
		} else {
			data, err := ioutil.ReadAll(f)
			if err != nil {
				return err
			}

			if err := c.writeFileToDest(file.Name, data); err != nil {
				return err
			}
		}
	}

	if c.Debug {
		log.Printf("Zipfile successfully extracted to %s", c.Output)
	}

	return nil
}

// downloadZip fetches the rest-resources artifact from a Build and return its content as []byte.
func (c Command) downloadZip(b Build) ([]byte, error) {
	url := b.zipfileUrl()
	if c.Debug {
		log.Printf("Zipfile url : %s", b.zipfileUrl())
	}

	client := &http.Client{}

	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Accept-Content", "gzip")
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	data, _ := ioutil.ReadAll(res.Body)
	return data, err
}

// findMostRecentBuild iterates through the builds retrieved from the api
// and return the latest one based on the StartTime of each Build.
func findMostRecentBuild(builds []Build) Build {
	var latestBuild Build
	latestBuild = NewBuild()
	for _, build := range builds {
		if build.StartTime.After(*latestBuild.StartTime.Time) {
			latestBuild = build
		}
	}
	return latestBuild
}

// findBuildByCommitHash iterates through the builds and returns the first occurrence of Build
// that matches the provided commitHash.
func findBuildByCommitHash(commitHash string, builds []Build) (Build, error) {
	for _, build := range builds {
		if build.Projects.Elasticsearch.CommitHash == commitHash {
			return build, nil
		}
	}
	return Build{}, fmt.Errorf("Build with commit hash %s not found", commitHash)
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/TimothyYe/godns/internal/settings"
	"github.com/TimothyYe/godns/internal/utils"
	"github.com/TimothyYe/godns/pkg/notify"
	"io"
	"io/ioutil"
	"net/http"
	"runtime/debug"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

// Handler struct definition
type Handler struct {
	Configuration *settings.Settings
	API           string
}

// DNSRecordResponse struct
type DNSRecordResponse struct {
	Records []DNSRecord `json:"result"`
	Success bool        `json:"success"`
}

// DNSRecordUpdateResponse struct
type DNSRecordUpdateResponse struct {
	Record  DNSRecord `json:"result"`
	Success bool      `json:"success"`
}

// DNSRecord for Cloudflare API
type DNSRecord struct {
	ID      string `json:"id"`
	IP      string `json:"content"`
	Name    string `json:"name"`
	Proxied bool   `json:"proxied"`
	Type    string `json:"type"`
	ZoneID  string `json:"zone_id"`
	TTL     int32  `json:"ttl"`
}

// SetIP updates DNSRecord.IP
func (r *DNSRecord) SetIP(ip string) {
	r.IP = ip
}

// ZoneResponse is a wrapper for Zones
type ZoneResponse struct {
	Zones   []Zone `json:"result"`
	Success bool   `json:"success"`
}

// Zone object with id and name
type Zone struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *settings.Settings) {
	handler.Configuration = conf
	handler.API = "https://api.cloudflare.com/client/v4"
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *settings.Domain, panicChan chan<- settings.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Errorf("Recovered in %v: %v\n", err, string(debug.Stack()))
			panicChan <- *domain
		}
	}()

	var lastIP string
	looping := false
	for {
		if looping {
			// Sleep with interval
			log.Debugf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
			time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
		}
		looping = true

		currentIP, err := utils.GetCurrentIP(handler.Configuration)
		if err != nil {
			log.Error("Error in GetCurrentIP:", err)
			continue
		}
		log.Debug("Current IP is:", currentIP)
		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Infof("IP is the same as cached one (%s). Skip update.\n", currentIP)
		} else {
			log.Info("Checking IP for domain", domain.DomainName)
			zoneID := handler.getZone(domain.DomainName)
			if zoneID != "" {
				records := handler.getDNSRecords(zoneID)

				// update records
				for _, rec := range records {
					if !recordTracked(domain, &rec) {
						log.Debug("Skiping record:", rec.Name)
						continue
					}
					if rec.IP != currentIP {
						log.Infof("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP)
						lastIP = handler.updateRecord(rec, currentIP)

						// Send notification
						notify.GetNotifyManager(handler.Configuration).Send(rec.Name, currentIP)
					} else {
						log.Infof("Record OK: %+v - %+v\r\n", rec.Name, rec.IP)
					}
				}
			} else {
				log.Info("Failed to find zone for domain:", domain.DomainName)
			}
		}
	}
}

// Check if record is present in domain conf
func recordTracked(domain *settings.Domain, record *DNSRecord) bool {
	for _, subDomain := range domain.SubDomains {
		sd := fmt.Sprintf("%s.%s", subDomain, domain.DomainName)
		if record.Name == sd {
			return true
		} else if subDomain == utils.RootDomain && record.Name == domain.DomainName {
			return true
		}
	}

	return false
}

// Create a new request with auth in place and optional proxy
func (handler *Handler) newRequest(method, url string, body io.Reader) (*http.Request, *http.Client) {
	client := utils.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
	if client == nil {
		log.Info("cannot create HTTP client")
	}

	req, _ := http.NewRequest(method, handler.API+url, body)
	req.Header.Set("Content-Type", "application/json")

	if handler.Configuration.Email != "" && handler.Configuration.Password != "" {
		req.Header.Set("X-Auth-Email", handler.Configuration.Email)
		req.Header.Set("X-Auth-Key", handler.Configuration.Password)
	} else if handler.Configuration.LoginToken != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", handler.Configuration.LoginToken))
	}

	return req, client
}

// Find the correct zone via domain name
func (handler *Handler) getZone(domain string) string {

	var z ZoneResponse

	req, client := handler.newRequest("GET", fmt.Sprintf("/zones?name=%s", domain), nil)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return ""
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &z)
	if err != nil {
		log.Errorf("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return ""
	}
	if z.Success != true {
		log.Infof("Response failed: %+v\n", string(body))
		return ""
	}

	for _, zone := range z.Zones {
		if zone.Name == domain {
			return zone.ID
		}
	}
	return ""
}

// Get all DNS A records for a zone
func (handler *Handler) getDNSRecords(zoneID string) []DNSRecord {

	var empty []DNSRecord
	var r DNSRecordResponse
	var recordType string

	if handler.Configuration.IPType == "" || strings.ToUpper(handler.Configuration.IPType) == utils.IPV4 {
		recordType = utils.IPTypeA
	} else if strings.ToUpper(handler.Configuration.IPType) == utils.IPV6 {
		recordType = utils.IPTypeAAAA
	}

	log.Info("Querying records with type:", recordType)
	req, client := handler.newRequest("GET", fmt.Sprintf("/zones/"+zoneID+"/dns_records?type=%s&page=1&per_page=500", recordType), nil)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return empty
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &r)
	if err != nil {
		log.Infof("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return empty
	}
	if r.Success != true {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Infof("Response failed: %+v\n", string(body))
		return empty

	}
	return r.Records
}

// Update DNS A Record with new IP
func (handler *Handler) updateRecord(record DNSRecord, newIP string) string {

	var r DNSRecordUpdateResponse
	record.SetIP(newIP)
	var lastIP string

	j, _ := json.Marshal(record)
	req, client := handler.newRequest("PUT",
		"/zones/"+record.ZoneID+"/dns_records/"+record.ID,
		bytes.NewBuffer(j),
	)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return ""
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &r)
	if err != nil {
		log.Errorf("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return ""
	}
	if r.Success != true {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Infof("Response failed: %+v\n", string(body))
	} else {
		log.Infof("Record updated: %+v - %+v", record.Name, record.IP)
		lastIP = record.IP
	}
	return lastIP
}
<commit_msg>Fix log message: space between word and domain (#133)<commit_after>package cloudflare

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/TimothyYe/godns/internal/settings"
	"github.com/TimothyYe/godns/internal/utils"
	"github.com/TimothyYe/godns/pkg/notify"
	"io"
	"io/ioutil"
	"net/http"
	"runtime/debug"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

// Handler struct definition
type Handler struct {
	Configuration *settings.Settings
	API           string
}

// DNSRecordResponse struct
type DNSRecordResponse struct {
	Records []DNSRecord `json:"result"`
	Success bool        `json:"success"`
}

// DNSRecordUpdateResponse struct
type DNSRecordUpdateResponse struct {
	Record  DNSRecord `json:"result"`
	Success bool      `json:"success"`
}

// DNSRecord for Cloudflare API
type DNSRecord struct {
	ID      string `json:"id"`
	IP      string `json:"content"`
	Name    string `json:"name"`
	Proxied bool   `json:"proxied"`
	Type    string `json:"type"`
	ZoneID  string `json:"zone_id"`
	TTL     int32  `json:"ttl"`
}

// SetIP updates DNSRecord.IP
func (r *DNSRecord) SetIP(ip string) {
	r.IP = ip
}

// ZoneResponse is a wrapper for Zones
type ZoneResponse struct {
	Zones   []Zone `json:"result"`
	Success bool   `json:"success"`
}

// Zone object with id and name
type Zone struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

// SetConfiguration pass dns settings and store it to handler instance
func (handler *Handler) SetConfiguration(conf *settings.Settings) {
	handler.Configuration = conf
	handler.API = "https://api.cloudflare.com/client/v4"
}

// DomainLoop the main logic loop
func (handler *Handler) DomainLoop(domain *settings.Domain, panicChan chan<- settings.Domain) {
	defer func() {
		if err := recover(); err != nil {
			log.Errorf("Recovered in %v: %v\n", err, string(debug.Stack()))
			panicChan <- *domain
		}
	}()

	var lastIP string
	looping := false
	for {
		if looping {
			// Sleep with interval
			log.Debugf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
			time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
		}
		looping = true

		currentIP, err := utils.GetCurrentIP(handler.Configuration)
		if err != nil {
			log.Error("Error in GetCurrentIP:", err)
			continue
		}
		log.Debug("Current IP is:", currentIP)
		//check against locally cached IP, if no change, skip update
		if currentIP == lastIP {
			log.Infof("IP is the same as cached one (%s). Skip update.\n", currentIP)
		} else {
			log.Info("Checking IP for domain ", domain.DomainName)
			zoneID := handler.getZone(domain.DomainName)
			if zoneID != "" {
				records := handler.getDNSRecords(zoneID)

				// update records
				for _, rec := range records {
					if !recordTracked(domain, &rec) {
						log.Debug("Skiping record:", rec.Name)
						continue
					}
					if rec.IP != currentIP {
						log.Infof("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP)
						lastIP = handler.updateRecord(rec, currentIP)

						// Send notification
						notify.GetNotifyManager(handler.Configuration).Send(rec.Name, currentIP)
					} else {
						log.Infof("Record OK: %+v - %+v\r\n", rec.Name, rec.IP)
					}
				}
			} else {
				log.Info("Failed to find zone for domain:", domain.DomainName)
			}
		}
	}
}

// Check if record is present in domain conf
func recordTracked(domain *settings.Domain, record *DNSRecord) bool {
	for _, subDomain := range domain.SubDomains {
		sd := fmt.Sprintf("%s.%s", subDomain, domain.DomainName)
		if record.Name == sd {
			return true
		} else if subDomain == utils.RootDomain && record.Name == domain.DomainName {
			return true
		}
	}

	return false
}

// Create a new request with auth in place and optional proxy
func (handler *Handler) newRequest(method, url string, body io.Reader) (*http.Request, *http.Client) {
	client := utils.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
	if client == nil {
		log.Info("cannot create HTTP client")
	}

	req, _ := http.NewRequest(method, handler.API+url, body)
	req.Header.Set("Content-Type", "application/json")

	if handler.Configuration.Email != "" && handler.Configuration.Password != "" {
		req.Header.Set("X-Auth-Email", handler.Configuration.Email)
		req.Header.Set("X-Auth-Key", handler.Configuration.Password)
	} else if handler.Configuration.LoginToken != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", handler.Configuration.LoginToken))
	}

	return req, client
}

// Find the correct zone via domain name
func (handler *Handler) getZone(domain string) string {

	var z ZoneResponse

	req, client := handler.newRequest("GET", fmt.Sprintf("/zones?name=%s", domain), nil)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return ""
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &z)
	if err != nil {
		log.Errorf("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return ""
	}
	if z.Success != true {
		log.Infof("Response failed: %+v\n", string(body))
		return ""
	}

	for _, zone := range z.Zones {
		if zone.Name == domain {
			return zone.ID
		}
	}
	return ""
}

// Get all DNS A records for a zone
func (handler *Handler) getDNSRecords(zoneID string) []DNSRecord {

	var empty []DNSRecord
	var r DNSRecordResponse
	var recordType string

	if handler.Configuration.IPType == "" || strings.ToUpper(handler.Configuration.IPType) == utils.IPV4 {
		recordType = utils.IPTypeA
	} else if strings.ToUpper(handler.Configuration.IPType) == utils.IPV6 {
		recordType = utils.IPTypeAAAA
	}

	log.Info("Querying records with type:", recordType)
	req, client := handler.newRequest("GET", fmt.Sprintf("/zones/"+zoneID+"/dns_records?type=%s&page=1&per_page=500", recordType), nil)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return empty
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &r)
	if err != nil {
		log.Infof("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return empty
	}
	if r.Success != true {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Infof("Response failed: %+v\n", string(body))
		return empty

	}
	return r.Records
}

// Update DNS A Record with new IP
func (handler *Handler) updateRecord(record DNSRecord, newIP string) string {

	var r DNSRecordUpdateResponse
	record.SetIP(newIP)
	var lastIP string

	j, _ := json.Marshal(record)
	req, client := handler.newRequest("PUT",
		"/zones/"+record.ZoneID+"/dns_records/"+record.ID,
		bytes.NewBuffer(j),
	)
	resp, err := client.Do(req)
	if err != nil {
		log.Error("Request error:", err)
		return ""
	}

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &r)
	if err != nil {
		log.Errorf("Decoder error: %+v\n", err)
		log.Debugf("Response body: %+v\n", string(body))
		return ""
	}
	if r.Success != true {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Infof("Response failed: %+v\n", string(body))
	} else {
		log.Infof("Record updated: %+v - %+v", record.Name, record.IP)
		lastIP = record.IP
	}
	return lastIP
}
<|endoftext|>


<commit_before>package player

import (
	"github.com/Joey92/poker-player-deathstar/leanpoker"
)

const VERSION = "Default Go folding player"

func BetRequest(state *leanpoker.Game) int {

	p := state.GetPlayer()

	if p == nil {
		return 1000
	}

	var bet int

	if (state.IsCheckable(bet)) {
		return bet
	}

	if isPair(p.HoleCards) {
		bet += 1000
	}

	return bet
}

func Showdown(state *leanpoker.Game) {

}

func Version() string {
	return VERSION
}

func isPair(cards []*leanpoker.Card) bool  {
	if len(cards) < 2 {
		return false
	}

	return cards[0].Rank == cards[1].Rank
}

<commit_msg>Min buy-in on pair<commit_after>package player

import (
	"github.com/Joey92/poker-player-deathstar/leanpoker"
)

const VERSION = "Default Go folding player"

func BetRequest(state *leanpoker.Game) int {

	p := state.GetPlayer()

	if p == nil {
		return 1000
	}

	var bet int

	if (state.IsCheckable(bet)) {
		return bet
	}

	if isPair(p.HoleCards) {
		bet += state.CurrentBuyIn * 2
	}

	/*if (len(state.CommunityCards) > 0) {
		bet += calcComunityCards(state, bet)
	}*/

	return bet
}

func Showdown(state *leanpoker.Game) {

}

func Version() string {
	return VERSION
}

func isPair(cards []*leanpoker.Card) bool  {
	if len(cards) < 2 {
		return false
	}

	return cards[0].Rank == cards[1].Rank
}

/*func calcComunityCards(g *leanpoker.Game, currentBet) {

}*/

<|endoftext|>


<commit_before>package player

import "github.com/krig/go-sox"
import (
	"errors"
	"sync"
	"time"
)

type Player struct {
	WG *sync.WaitGroup
	sync.Mutex
	State *State
}

// InOut struct holds the state of the player
type State struct {
	in        *sox.Format
	out       *sox.Format
	chain     *sox.EffectsChain
	status    int
	startTime time.Time
	durationPaused time.Duration
	queue     []string
	current   int
}

const (
	Playing = iota
	Paused
	Waiting
)

// Starts sox and the initialises player's state
func (player *Player) init() error {
	player.Lock()
	defer player.Unlock()
	if !sox.Init() {
		return errors.New(no_sox_msg)
	}
	player.State = new(State)
	player.State.status = Waiting
	return nil
}

// Clears resources
func (player *Player) clear() {
	player.Lock()
	defer player.Unlock()
	sox.Quit()
	if player.State.in != nil {
		player.State.in.Release()
	}

	if player.State.out != nil {
		player.State.out.Release()
	}

	if player.State.chain != nil {
		player.State.chain.Release()
	}
}

// Plays single file
func (player *Player) playSingleFile(filename string, trim int) error {
	// Open the input file (with default parameters)
	in := sox.OpenRead(filename)
	if in == nil {
		if player.WG != nil {
			player.WG.Done()
		}
		return errors.New(no_sox_in_msg)
	}
	player.State.in = in

	// Open the output device: Specify the output signal characteristics.
	// Since we are using only simple effects, they are the same as the
	// input file characteristics.
	// Using "alsa" or "pulseaudio" should work for most files on Linux.
	// "coreaudio" for OSX
	// On other systems, other devices have to be used.
	out := sox.OpenWrite("default", in.Signal(), nil, "alsa")
	if out == nil {
		out = sox.OpenWrite("default", in.Signal(), nil, "pulseaudio")
		if out == nil {
			out = sox.OpenWrite("default", in.Signal(), nil, "coreaudio")
			if out == nil {
				out = sox.OpenWrite("default", in.Signal(), nil, "waveaudio")
				if out == nil {
					if player.WG != nil {
						player.WG.Done()
					}
					return errors.New(no_sox_out_msg)
				}
			}
		}
	}
	player.State.out = out

	// Create an effects chain: Some effects need to know about the
	// input or output encoding so we provide that information here.
	chain := sox.CreateEffectsChain(in.Encoding(), out.Encoding())
	player.State.chain = chain

	// The first effect in the effect chain must be something that can
	// source samples; in this case, we use the built-in handler that
	// inputs data from an audio file.
	e := sox.CreateEffect(sox.FindEffect("input"))
	e.Options(in)
	// This becomes the first "effect" in the chain
	chain.Add(e, in.Signal(), in.Signal())
	e.Release()

	if trim > 0 {
		interm_signal := in.Signal().Copy()

		e = sox.CreateEffect(sox.FindEffect("trim"))
		e.Options(trim) //todo: try with float ?!?
		chain.Add(e, interm_signal, in.Signal())
		e.Release()
	}

	// The last effect in the effect chain must be something that only consumes
	// samples; in this case, we use the built-in handler that outputs data.
	e = sox.CreateEffect(sox.FindEffect("output"))
	e.Options(out)
	chain.Add(e, in.Signal(), in.Signal())
	e.Release()

	player.State.status = Playing
	player.State.startTime = time.Now()

	// Flow samples through the effects processing chain until EOF is reached.
	go func(wg *sync.WaitGroup) {
		chain.Flow()
		if wg != nil {
			wg.Done()
		}
		player.State.status = Waiting
	}(player.WG)

	return nil
}

func (player *Player) play(playItem string) error{
	player.Lock()
	defer player.Unlock()

	// todo: what is playItem
	// todo: manage queue
	// todo: play all items

	return nil
}

func (player *Player) pause() error {
	player.Lock()
	defer player.Unlock()

	if player.State.status != Playing {
		return errors.New("Cannot pause. Player is not playing")
	}

	player.State.chain.DeleteAll()
	player.State.durationPaused = time.Since(player.State.startTime)
	player.State.status = Paused

	return nil
}

func (player *Player) resume() error {
	player.Lock()
	defer player.Unlock()

	if player.State.status != Paused {
		return errors.New("Cannot resume. Player is not paused")
	}

	songToResume := player.State.queue[player.State.current]
	pausedDuration := player.State.durationPaused

	//todo: some queue management
	return player.playSingleFile(songToResume, pausedDuration.Seconds())
}

//func main() {
//	wg := sync.WaitGroup{}
//	player = Player{WG:&wg}
//	err := player.init()
//	if (err != nil) {
//		return
//	}
//	fmt.Println("Adding")
//	wg.Add(1)
//	player.playSingleFile("/Users/katyaspasova/Music/Vampolka.mp3")
//	wg.Wait()
//	player.clear()
//}
<commit_msg>add queue management and read from directory (not tested)<commit_after>package player

import "github.com/krig/go-sox"
import (
	"errors"
	"os"
	"strings"
	"sync"
	"time"
)

const playlistsDir = "playlists" + os.PathSeparator
const playlistsExtension = ".m3u"

type Player struct {
	WG *sync.WaitGroup
	sync.Mutex
	State *State
}

// InOut struct holds the state of the player
type State struct {
	in             *sox.Format
	out            *sox.Format
	chain          *sox.EffectsChain
	status         int
	startTime      time.Time
	durationPaused time.Duration
	queue          []string
	current        int
}

const (
	Playing = iota
	Paused
	Waiting
)

// Starts sox and the initialises player's state
func (player *Player) init() error {
	player.Lock()
	defer player.Unlock()
	if !sox.Init() {
		return errors.New(no_sox_msg)
	}
	player.State = new(State)
	player.State.status = Waiting
	player.State.current = 0
	player.State.queue = make(string, 0)
	return nil
}

// Clears resources
func (player *Player) clear() {
	player.Lock()
	defer player.Unlock()
	sox.Quit()
	if player.State.in != nil {
		player.State.in.Release()
	}

	if player.State.out != nil {
		player.State.out.Release()
	}

	if player.State.chain != nil {
		player.State.chain.Release()
	}
}

// Plays single file
func (player *Player) playSingleFile(filename string, trim float32) error {
	// Open the input file (with default parameters)
	in := sox.OpenRead(filename)
	if in == nil {
		if player.WG != nil {
			player.WG.Done()
		}
		return errors.New(no_sox_in_msg)
	}
	player.State.in = in

	// Open the output device: Specify the output signal characteristics.
	// Since we are using only simple effects, they are the same as the
	// input file characteristics.
	// Using "alsa" or "pulseaudio" should work for most files on Linux.
	// "coreaudio" for OSX
	// On other systems, other devices have to be used.
	out := sox.OpenWrite("default", in.Signal(), nil, "alsa")
	if out == nil {
		out = sox.OpenWrite("default", in.Signal(), nil, "pulseaudio")
		if out == nil {
			out = sox.OpenWrite("default", in.Signal(), nil, "coreaudio")
			if out == nil {
				out = sox.OpenWrite("default", in.Signal(), nil, "waveaudio")
				if out == nil {
					if player.WG != nil {
						player.WG.Done()
					}
					return errors.New(no_sox_out_msg)
				}
			}
		}
	}
	player.State.out = out

	// Create an effects chain: Some effects need to know about the
	// input or output encoding so we provide that information here.
	chain := sox.CreateEffectsChain(in.Encoding(), out.Encoding())
	player.State.chain = chain

	// The first effect in the effect chain must be something that can
	// source samples; in this case, we use the built-in handler that
	// inputs data from an audio file.
	e := sox.CreateEffect(sox.FindEffect("input"))
	e.Options(in)
	// This becomes the first "effect" in the chain
	chain.Add(e, in.Signal(), in.Signal())
	e.Release()

	if trim > 0 {
		interm_signal := in.Signal().Copy()

		e = sox.CreateEffect(sox.FindEffect("trim"))
		e.Options(trim) //todo: try with float ?!?
		chain.Add(e, interm_signal, in.Signal())
		e.Release()
	}

	// The last effect in the effect chain must be something that only consumes
	// samples; in this case, we use the built-in handler that outputs data.
	e = sox.CreateEffect(sox.FindEffect("output"))
	e.Options(out)
	chain.Add(e, in.Signal(), in.Signal())
	e.Release()

	player.State.status = Playing
	player.State.startTime = time.Now()

	// Flow samples through the effects processing chain until EOF is reached.
	go func(wg *sync.WaitGroup) {
		chain.Flow()
		if wg != nil {
			wg.Done()
		}
		player.State.status = Waiting
	}(player.WG)

	return nil
}

func (player *Player) play(playItem string) ([]string, error) {
	player.Lock()

	items, err := player.addPlayItem(playItem)
	player.Unlock()

	// play all items
	if err != nil {
		go player.playQueue(0)
	}
	return items, err
}

func (player *Player) addPlayItem(playItem string) ([]string, error) {
	//todo: maybe return the parsed items as slice

	// is it file or directory
	fileInfo, err := os.Stat(playItem)
	if os.IsNotExist(err) != nil {
		//try it for a playlist
		fileInfo, err = os.Stat(playlistsDir + playItem + ".m3u")
		if os.IsNotExist(err) {
			return nil, errors.New(playItem + " does not exist")
		}
	}

	items := make([]string, 0)

	switch mode := fileInfo.Mode(); {
	case mode.IsDir():

		d, err := os.Open(playItem)
		if err != nil {
			return nil, err
		}
		defer d.Close()
		files, err := d.Readdir(-1)
		if err != nil {
			return nil, err
		}

		for _, file := range files {
			if file.Mode().IsRegular() {
				added := player.addRegularFile(file.Name())
				items = append(items, added...)
			}
		}
	case mode.IsRegular():
		added := player.addRegularFile(playItem)
		items = append(items, added...)

	}
	return items, nil
}

func (player *Player) addRegularFile(playItem string) []string {
	items := make([]string, 0)
	if strings.HasSuffix(playItem, playlistsExtension) {
		//todo: parse it
	} else {
		name, err := player.addFile(playItem)
		// file is not suported - simply skip it
		if err != nil {
			items = append(items, name)
		}
	}
	return items
}

// Adds a single file to the player queue
// Checks if file type is supported
func (player *Player) addFile(file string) (string, error) {
	//todo: check if file type is supported
	player.State.queue = append(player.State.queue, file)
	return file, nil
}

func (player *Player) playQueue(trim float32) {
	play := true
	for play {
		var fileName string
		player.Lock()
		index := player.State.current
		if index < len(player.State.queue) {
			fileName = player.State.queue[index]
		} else {
			play = false
			player.State.current = 0
			player.State.status = Waiting
		}
		player.Unlock()

		if play && len(fileName) > 0 {
			player.playSingleFile(fileName, trim)
		}
		player.Lock()
		player.State.current += 1
		player.Unlock()
	}
}

func (player *Player) pause() error {
	player.Lock()
	defer player.Unlock()

	if player.State.status != Playing {
		return errors.New("Cannot pause. Player is not playing")
	}

	player.State.chain.DeleteAll()
	player.State.durationPaused = time.Since(player.State.startTime)
	player.State.status = Paused

	return nil
}

func (player *Player) resume() error {
	player.Lock()

	if player.State.status != Paused {
		return errors.New("Cannot resume. Player is not paused")
	}

	songToResume := player.State.queue[player.State.current]
	pausedDuration := player.State.durationPaused
	player.Unlock()

	go player.playQueue(pausedDuration.Seconds())
	return nil
}

func (player *Player) addToQueue(item string) error {
	player.Lock()
	defer player.Unlock()

	//todo: determine what item it

	//todo: add to queue

	//start playing if in Waiting status
	if player.State.status == Waiting {
		go player.playQueue(0)
	}

	return nil
}

func (player *Player) stop() {
	player.Lock()
	defer player.Unlock()
	player.State.chain.DeleteAll()
	player.State.status = Waiting
	player.State.current = 0
	player.State.queue = make(string, 0)
}

//func main() {
//	wg := sync.WaitGroup{}
//	player = Player{WG:&wg}
//	err := player.init()
//	if (err != nil) {
//		return
//	}
//	fmt.Println("Adding")
//	wg.Add(1)
//	player.playSingleFile("/Users/katyaspasova/Music/Vampolka.mp3")
//	wg.Wait()
//	player.clear()
//}
<|endoftext|>


<commit_before>package workqueue

import (
	"runtime"
	"sync"
	"time"
)

type Work struct {
	Data   interface{}
	Action func(*Work) int
	Ch     interface{}
}

type WorkQueue struct {
	mainCond     *sync.Cond
	routinesCond *sync.Cond

	routinesLock  sync.RWMutex
	totalRoutines int
	freeRoutines  int

	listRWLock  sync.RWMutex
	workList    []*Work
	workNumbers int
}

func (wq *WorkQueue) wakeupMain() {
	wq.mainCond.Signal()
}

func (wq *WorkQueue) ScheduleWork(work *Work) {
	wq.listRWLock.Lock()
	wq.workList = append(wq.workList, work)
	wq.workNumbers++
	wq.listRWLock.Unlock()

	wq.wakeupMain()
}

func CreateWorkQueue(routinesNumber int) *WorkQueue {
	runtime.GOMAXPROCS(runtime.NumCPU())

	wq := new(WorkQueue)

	done := make(chan int)
	go func() {
		locker := new(sync.Mutex)
		wq.mainCond = sync.NewCond(locker)

		locker = new(sync.Mutex)
		wq.routinesCond = sync.NewCond(locker)

		done <- 0
		for {
			wq.mainCond.L.Lock()
			wq.mainCond.Wait()
			wq.mainCond.L.Unlock()

			wq.listRWLock.Lock()
			workNumbers := wq.workNumbers
			wq.listRWLock.Unlock()

			wq.routinesLock.Lock()
			freeRoutines := wq.freeRoutines
			wq.routinesLock.Unlock()

			var wakeupRoutines int
			var needSchedule bool
			if freeRoutines > workNumbers {
				wakeupRoutines = workNumbers
				needSchedule = false
			} else {
				wakeupRoutines = workNumbers - freeRoutines
				needSchedule = true
			}

			for ; wakeupRoutines > 0; wakeupRoutines-- {
				wq.routinesCond.L.Lock()
				wq.routinesCond.Signal()
				wq.routinesCond.L.Unlock()
			}

			if needSchedule {
				time.Sleep(5 * time.Second)
			}
		}
	}()

	<-done
	for i := 0; i < routinesNumber; i++ {
		go func(i int) {
			for {
				wq.routinesCond.L.Lock()

				wq.routinesLock.Lock()
				wq.freeRoutines++
				wq.routinesLock.Unlock()

				wq.routinesCond.Wait()

				wq.routinesLock.Lock()
				wq.freeRoutines--
				wq.routinesLock.Unlock()

				wq.routinesCond.L.Unlock()

				wq.listRWLock.Lock()
				work := wq.workList[0]
				wq.workList = wq.workList[1:]
				wq.workNumbers--
				wq.listRWLock.Unlock()

				work.Action(work)
			}
		}(i)
	}

	return wq
}

func (wq *WorkQueue) DestroyWorkqueue() {
}
<commit_msg>1, wrap some lock to function 2, add schedule at moment of wait goroutines free<commit_after>package workqueue

import (
	"runtime"
	"sync"
)

type Work struct {
	Data   interface{}
	Action func(*Work) int
	Ch     interface{}
}

type WorkQueue struct {
	mainCond     *sync.Cond
	routinesCond *sync.Cond

	routinesLock sync.RWMutex
	freeRoutines int

	listRWLock  sync.RWMutex
	workList    []*Work
	workNumbers int
}

func (wq *WorkQueue) waitScheduleWork() {
	wq.mainCond.L.Lock()
	wq.mainCond.Wait()
	wq.mainCond.L.Unlock()
}

func (wq *WorkQueue) wakeupMainRoutine() {
	wq.mainCond.L.Lock()
	wq.mainCond.Signal()
	wq.mainCond.L.Unlock()
}

func (wq *WorkQueue) waitMainSchedule() {
	wq.routinesCond.L.Lock()

	wq.routinesLock.Lock()
	wq.freeRoutines++
	wq.routinesLock.Unlock()

	wq.routinesCond.Wait()

	wq.routinesLock.Lock()
	wq.freeRoutines--
	wq.routinesLock.Unlock()

	wq.routinesCond.L.Unlock()
}

func (wq *WorkQueue) wakeupRoutines() {
	wq.routinesCond.L.Lock()
	wq.routinesCond.Signal()
	wq.routinesCond.L.Unlock()
}

func (wq *WorkQueue) fetchWork() *Work {
	var work *Work = nil

	wq.listRWLock.Lock()
	if wq.workNumbers > 0 {
		work = wq.workList[0]
		if wq.workNumbers == 1 {
			wq.workList = []*Work{}
		} else {
			wq.workList = wq.workList[1:]
		}
		wq.workNumbers--
	}

	wq.listRWLock.Unlock()

	return work
}

func (wq *WorkQueue) schedulableWorks(works *int, schedule *bool) {
	wq.listRWLock.Lock()
	workNumbers := wq.workNumbers
	wq.listRWLock.Unlock()

	wq.routinesLock.Lock()
	freeRoutines := wq.freeRoutines
	wq.routinesLock.Unlock()

	if freeRoutines >= workNumbers {
		*works = workNumbers
		*schedule = false
	} else {
		*works = workNumbers - freeRoutines
		*schedule = true
	}
}

func (wq *WorkQueue) ScheduleWork(work *Work) {
	wq.listRWLock.Lock()
	wq.workList = append(wq.workList, work)
	wq.workNumbers++
	wq.listRWLock.Unlock()

	wq.wakeupMainRoutine()
}

func CreateWorkQueue(routinesNumber int) *WorkQueue {
	runtime.GOMAXPROCS(runtime.NumCPU())

	wq := new(WorkQueue)

	done := make(chan int)
	go func() {
		locker := new(sync.Mutex)
		wq.mainCond = sync.NewCond(locker)

		locker = new(sync.Mutex)
		wq.routinesCond = sync.NewCond(locker)

		var needSchedule bool
		var wakeupRoutines int
		done <- 0
		for {
			if !needSchedule {
				wq.waitScheduleWork()
			}

			wq.schedulableWorks(&wakeupRoutines, &needSchedule)

			for ; wakeupRoutines > 0; wakeupRoutines-- {
				wq.wakeupRoutines()
			}

			if needSchedule {
				runtime.Gosched()
			}
		}
	}()

	<-done
	for i := 0; i < routinesNumber; i++ {
		go func() {
			for {
				wq.waitMainSchedule()

				work := wq.fetchWork()

				if work != nil {
					work.Action(work)
				}
			}
		}()
	}

	return wq
}

func (wq *WorkQueue) DestroyWorkqueue() {
}
<|endoftext|>