<commit_before>package exprel

import (
	"math"
)

type node interface {
	Evaluate(s Source) interface{}
}

type stringNode string

func (n stringNode) Evaluate(s Source) interface{} {
	return string(n)
}

type boolNode bool

func (n boolNode) Evaluate(s Source) interface{} {
	return bool(n)
}

type numberNode float64

func (n numberNode) Evaluate(s Source) interface{} {
	return float64(n)
}

type notNode struct {
	node
}

func (n *notNode) Evaluate(s Source) interface{} {
	val, ok := n.node.Evaluate(s).(bool)
	if !ok {
		re("NOT expects bool value")
	}
	return !val
}

type lookupNode string

func (n lookupNode) Evaluate(s Source) interface{} {
	id := string(n)
	ret, ok := s.Get(id)
	if !ok {
		re("unknown identifier " + id)
	}
	switch ret.(type) {
	case string, bool, float64:
	default:
		re("identifier '" + id + "' has invalid type")
	}
	return ret
}

type callNode struct {
	Name string
	Args []node
}

func (n *callNode) Evaluate(s Source) interface{} {
	name := n.Name
	fnValue, ok := s.Get(name)
	if !ok {
		re("unknown function " + name)
	}
	fn, ok := fnValue.(Func)
	if !ok {
		fn2, ok2 := fnValue.(func(*Call) (interface{}, error))
		if !ok2 {
			re("cannot call non-function " + name)
		}
		fn = fn2
	}
	call := Call{
		Name:   name,
		Values: make([]interface{}, len(n.Args)),
	}
	for i, arg := range n.Args {
		call.Values[i] = arg.Evaluate(s)
	}
	ret, err := fn(&call)
	if err != nil {
		panic(&RuntimeError{Err: err})
	}
	switch ret.(type) {
	case string, bool, float64:
		return ret
	default:
		re("invalid function return type")
	}
	panic("never called")
}

type concatNode [2]node

func (n concatNode) Evaluate(s Source) interface{} {
	lhs, lhsOk := n[0].Evaluate(s).(string)
	if !lhsOk {
		re("LHS of & must be string")
	}
	rhs, rhsOk := n[1].Evaluate(s).(string)
	if !rhsOk {
		re("RHS of & must be string")
	}
	return lhs + rhs
}

type mathNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *mathNode) Evaluate(s Source) interface{} {
	lhs, lhsOK := n.LHS.Evaluate(s).(float64)
	rhs, rhsOK := n.RHS.Evaluate(s).(float64)
	if !lhsOK || !rhsOK {
		re("invalid " + string(n.Op) + " operands")
	}
	switch n.Op {
	case tknAdd:
		return lhs + rhs
	case tknSubtract:
		return lhs - rhs
	case tknMultiply:
		return lhs * rhs
	case tknDivide:
		if rhs == 0 {
			re("attempted division by zero")
		}
		return lhs / rhs
	case tknPower:
		return math.Pow(lhs, rhs)
	case tknModulo:
		b := int(rhs)
		if b == 0 {
			re("attempted division by zero")
		}
		return float64(int(lhs) % b)
	default:
		panic("never triggered")
	}
}

type eqNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *eqNode) Evaluate(s Source) interface{} {
	lhs := n.LHS.Evaluate(s)
	rhs := n.RHS.Evaluate(s)
	{
		a, aOK := lhs.(string)
		b, bOK := rhs.(string)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	{
		a, aOK := lhs.(bool)
		b, bOK := rhs.(bool)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	{
		a, aOK := lhs.(float64)
		b, bOK := rhs.(float64)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	re("mismatched comparison operand types")
	panic("never called")
}

type cmpNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *cmpNode) Evaluate(s Source) interface{} {
	lhs := n.LHS.Evaluate(s)
	rhs := n.RHS.Evaluate(s)
	{
		a, aOK := lhs.(string)
		b, bOK := rhs.(string)
		if aOK && bOK {
			switch n.Op {
			case tknGreater:
				return a > b
			case tknGreaterEqual:
				return a >= b
			case tknLess:
				return a < b
			case tknLessEqual:
				return a <= b
			}
		}
	}
	{
		a, aOK := lhs.(float64)
		b, bOK := rhs.(float64)
		if aOK && bOK {
			switch n.Op {
			case tknGreater:
				return a > b
			case tknGreaterEqual:
				return a >= b
			case tknLess:
				return a < b
			case tknLessEqual:
				return a <= b
			}
		}
	}
	re("mismatched comparison operand types")
	panic("never called")
}

type andNode []node

func (n andNode) Evaluate(s Source) interface{} {
	for _, current := range n {
		value, ok := current.Evaluate(s).(bool)
		if !ok {
			re("AND must have boolean arguments")
		}
		if !value {
			return false
		}
	}
	return true
}

type orNode []node

func (n orNode) Evaluate(s Source) interface{} {
	for _, current := range n {
		value, ok := current.Evaluate(s).(bool)
		if !ok {
			re("OR must have boolean arguments")
		}
		if value {
			return true
		}
	}
	return false
}

type ifNode struct {
	Cond  node
	True  node
	False node
}

func (n *ifNode) Evaluate(s Source) interface{} {
	cond, ok := n.Cond.Evaluate(s).(bool)
	if !ok {
		re("IF condition must be boolean")
	}
	if cond {
		return n.True.Evaluate(s)
	}
	return n.False.Evaluate(s)
}
<commit_msg>use math.Mod for modulo<commit_after>package exprel

import (
	"math"
)

type node interface {
	Evaluate(s Source) interface{}
}

type stringNode string

func (n stringNode) Evaluate(s Source) interface{} {
	return string(n)
}

type boolNode bool

func (n boolNode) Evaluate(s Source) interface{} {
	return bool(n)
}

type numberNode float64

func (n numberNode) Evaluate(s Source) interface{} {
	return float64(n)
}

type notNode struct {
	node
}

func (n *notNode) Evaluate(s Source) interface{} {
	val, ok := n.node.Evaluate(s).(bool)
	if !ok {
		re("NOT expects bool value")
	}
	return !val
}

type lookupNode string

func (n lookupNode) Evaluate(s Source) interface{} {
	id := string(n)
	ret, ok := s.Get(id)
	if !ok {
		re("unknown identifier " + id)
	}
	switch ret.(type) {
	case string, bool, float64:
	default:
		re("identifier '" + id + "' has invalid type")
	}
	return ret
}

type callNode struct {
	Name string
	Args []node
}

func (n *callNode) Evaluate(s Source) interface{} {
	name := n.Name
	fnValue, ok := s.Get(name)
	if !ok {
		re("unknown function " + name)
	}
	fn, ok := fnValue.(Func)
	if !ok {
		fn2, ok2 := fnValue.(func(*Call) (interface{}, error))
		if !ok2 {
			re("cannot call non-function " + name)
		}
		fn = fn2
	}
	call := Call{
		Name:   name,
		Values: make([]interface{}, len(n.Args)),
	}
	for i, arg := range n.Args {
		call.Values[i] = arg.Evaluate(s)
	}
	ret, err := fn(&call)
	if err != nil {
		panic(&RuntimeError{Err: err})
	}
	switch ret.(type) {
	case string, bool, float64:
		return ret
	default:
		re("invalid function return type")
	}
	panic("never called")
}

type concatNode [2]node

func (n concatNode) Evaluate(s Source) interface{} {
	lhs, lhsOk := n[0].Evaluate(s).(string)
	if !lhsOk {
		re("LHS of & must be string")
	}
	rhs, rhsOk := n[1].Evaluate(s).(string)
	if !rhsOk {
		re("RHS of & must be string")
	}
	return lhs + rhs
}

type mathNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *mathNode) Evaluate(s Source) interface{} {
	lhs, lhsOK := n.LHS.Evaluate(s).(float64)
	rhs, rhsOK := n.RHS.Evaluate(s).(float64)
	if !lhsOK || !rhsOK {
		re("invalid " + string(n.Op) + " operands")
	}
	switch n.Op {
	case tknAdd:
		return lhs + rhs
	case tknSubtract:
		return lhs - rhs
	case tknMultiply:
		return lhs * rhs
	case tknDivide:
		if rhs == 0 {
			re("attempted division by zero")
		}
		return lhs / rhs
	case tknPower:
		return math.Pow(lhs, rhs)
	case tknModulo:
		return math.Mod(lhs, rhs)
	default:
		panic("never triggered")
	}
}

type eqNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *eqNode) Evaluate(s Source) interface{} {
	lhs := n.LHS.Evaluate(s)
	rhs := n.RHS.Evaluate(s)
	{
		a, aOK := lhs.(string)
		b, bOK := rhs.(string)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	{
		a, aOK := lhs.(bool)
		b, bOK := rhs.(bool)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	{
		a, aOK := lhs.(float64)
		b, bOK := rhs.(float64)
		if aOK && bOK {
			if n.Op == tknEquals {
				return a == b
			}
			return a != b
		}
	}
	re("mismatched comparison operand types")
	panic("never called")
}

type cmpNode struct {
	Op  rune
	LHS node
	RHS node
}

func (n *cmpNode) Evaluate(s Source) interface{} {
	lhs := n.LHS.Evaluate(s)
	rhs := n.RHS.Evaluate(s)
	{
		a, aOK := lhs.(string)
		b, bOK := rhs.(string)
		if aOK && bOK {
			switch n.Op {
			case tknGreater:
				return a > b
			case tknGreaterEqual:
				return a >= b
			case tknLess:
				return a < b
			case tknLessEqual:
				return a <= b
			}
		}
	}
	{
		a, aOK := lhs.(float64)
		b, bOK := rhs.(float64)
		if aOK && bOK {
			switch n.Op {
			case tknGreater:
				return a > b
			case tknGreaterEqual:
				return a >= b
			case tknLess:
				return a < b
			case tknLessEqual:
				return a <= b
			}
		}
	}
	re("mismatched comparison operand types")
	panic("never called")
}

type andNode []node

func (n andNode) Evaluate(s Source) interface{} {
	for _, current := range n {
		value, ok := current.Evaluate(s).(bool)
		if !ok {
			re("AND must have boolean arguments")
		}
		if !value {
			return false
		}
	}
	return true
}

type orNode []node

func (n orNode) Evaluate(s Source) interface{} {
	for _, current := range n {
		value, ok := current.Evaluate(s).(bool)
		if !ok {
			re("OR must have boolean arguments")
		}
		if value {
			return true
		}
	}
	return false
}

type ifNode struct {
	Cond  node
	True  node
	False node
}

func (n *ifNode) Evaluate(s Source) interface{} {
	cond, ok := n.Cond.Evaluate(s).(bool)
	if !ok {
		re("IF condition must be boolean")
	}
	if cond {
		return n.True.Evaluate(s)
	}
	return n.False.Evaluate(s)
}
<|endoftext|>


<commit_before>package eventlog

// Loggable describes objects that can be marshalled into Metadata for logging
type Loggable interface {
	Loggable() map[string]interface{}
}

type LoggableMap map[string]interface{}

func (l LoggableMap) Loggable() map[string]interface{} {
	return l
}
<commit_msg>wip Loggable func<commit_after>package eventlog

// Loggable describes objects that can be marshalled into Metadata for logging
type Loggable interface {
	Loggable() map[string]interface{}
}

type LoggableMap map[string]interface{}

func (l LoggableMap) Loggable() map[string]interface{} {
	return l
}

// Loggable converts a func into a Loggable
type LoggableF func() map[string]interface{}

func (l LoggableF) Loggable() map[string]interface{} {
	return l()
}

func Deferred(key string, f func() string) Loggable {
	function := func() map[string]interface{} {
		return map[string]interface{}{
			key: f(),
		}
	}
	return LoggableF(function)
}
<|endoftext|>


<commit_before>package ipfsaddr

import (
	"errors"

	ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr"

	path "github.com/ipfs/go-ipfs/path"
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
	circuit "gx/ipfs/QmVEPsD9h95ToAC7NJpYopFcXyjVJm35xV4tw43J5JdCnL/go-libp2p-circuit"
	peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)

var log = logging.Logger("ipfsaddr")

// ErrInvalidAddr signals an address is not a valid IPFS address.
var ErrInvalidAddr = errors.New("invalid IPFS address")

type IPFSAddr interface {
	ID() peer.ID
	Multiaddr() ma.Multiaddr
	Transport() ma.Multiaddr
	String() string
	Equal(b interface{}) bool
}

type ipfsAddr struct {
	ma ma.Multiaddr
	id peer.ID
}

func (a ipfsAddr) ID() peer.ID {
	return a.id
}

func (a ipfsAddr) Multiaddr() ma.Multiaddr {
	return a.ma
}

func (a ipfsAddr) Transport() ma.Multiaddr {
	return Transport(a)
}

func (a ipfsAddr) String() string {
	return a.ma.String()
}

func (a ipfsAddr) Equal(b interface{}) bool {
	if ib, ok := b.(IPFSAddr); ok {
		return a.Multiaddr().Equal(ib.Multiaddr())
	}
	if mb, ok := b.(ma.Multiaddr); ok {
		return a.Multiaddr().Equal(mb)
	}
	return false
}

// ParseString parses a string representation of an address into an IPFSAddr
func ParseString(str string) (a IPFSAddr, err error) {
	if str == "" {
		return nil, ErrInvalidAddr
	}

	m, err := ma.NewMultiaddr(str)
	if err != nil {
		return nil, err
	}

	return ParseMultiaddr(m)
}

// ParseMultiaddr parses a multiaddr into an IPFSAddr
func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) {
	// never panic.
	defer func() {
		if r := recover(); r != nil {
			log.Debug("recovered from panic: ", r)
			a = nil
			err = ErrInvalidAddr
		}
	}()

	if m == nil {
		return nil, ErrInvalidAddr
	}

	// make sure it's an IPFS addr
	parts := ma.Split(m)
	if len(parts) < 1 {
		return nil, ErrInvalidAddr
	}
	ipfspart := parts[len(parts)-1] // last part
	if ipfspart.Protocols()[0].Code != ma.P_IPFS {
		return nil, ErrInvalidAddr
	}

	// make sure 'ipfs id' parses as a peer.ID
	peerIdParts := path.SplitList(ipfspart.String())
	peerIdStr := peerIdParts[len(peerIdParts)-1]
	id, err := peer.IDB58Decode(peerIdStr)
	if err != nil {
		return nil, err
	}

	return ipfsAddr{ma: m, id: id}, nil
}

func Transport(iaddr IPFSAddr) (maddr ma.Multiaddr) {
	maddr = iaddr.Multiaddr()

	// /ipfs/QmId is part of the transport address for p2p-circuit
	_, err := maddr.ValueForProtocol(circuit.P_CIRCUIT)
	if err == nil {
		return maddr
	}

	split := ma.Split(maddr)
	maddr = ma.Join(split[:len(split)-1]...)
	return
}
<commit_msg>ipfsaddr: add TODO comment to clean up p2p-circuit special case<commit_after>package ipfsaddr

import (
	"errors"

	ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr"

	path "github.com/ipfs/go-ipfs/path"
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
	circuit "gx/ipfs/QmVEPsD9h95ToAC7NJpYopFcXyjVJm35xV4tw43J5JdCnL/go-libp2p-circuit"
	peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)

var log = logging.Logger("ipfsaddr")

// ErrInvalidAddr signals an address is not a valid IPFS address.
var ErrInvalidAddr = errors.New("invalid IPFS address")

type IPFSAddr interface {
	ID() peer.ID
	Multiaddr() ma.Multiaddr
	Transport() ma.Multiaddr
	String() string
	Equal(b interface{}) bool
}

type ipfsAddr struct {
	ma ma.Multiaddr
	id peer.ID
}

func (a ipfsAddr) ID() peer.ID {
	return a.id
}

func (a ipfsAddr) Multiaddr() ma.Multiaddr {
	return a.ma
}

func (a ipfsAddr) Transport() ma.Multiaddr {
	return Transport(a)
}

func (a ipfsAddr) String() string {
	return a.ma.String()
}

func (a ipfsAddr) Equal(b interface{}) bool {
	if ib, ok := b.(IPFSAddr); ok {
		return a.Multiaddr().Equal(ib.Multiaddr())
	}
	if mb, ok := b.(ma.Multiaddr); ok {
		return a.Multiaddr().Equal(mb)
	}
	return false
}

// ParseString parses a string representation of an address into an IPFSAddr
func ParseString(str string) (a IPFSAddr, err error) {
	if str == "" {
		return nil, ErrInvalidAddr
	}

	m, err := ma.NewMultiaddr(str)
	if err != nil {
		return nil, err
	}

	return ParseMultiaddr(m)
}

// ParseMultiaddr parses a multiaddr into an IPFSAddr
func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) {
	// never panic.
	defer func() {
		if r := recover(); r != nil {
			log.Debug("recovered from panic: ", r)
			a = nil
			err = ErrInvalidAddr
		}
	}()

	if m == nil {
		return nil, ErrInvalidAddr
	}

	// make sure it's an IPFS addr
	parts := ma.Split(m)
	if len(parts) < 1 {
		return nil, ErrInvalidAddr
	}
	ipfspart := parts[len(parts)-1] // last part
	if ipfspart.Protocols()[0].Code != ma.P_IPFS {
		return nil, ErrInvalidAddr
	}

	// make sure 'ipfs id' parses as a peer.ID
	peerIdParts := path.SplitList(ipfspart.String())
	peerIdStr := peerIdParts[len(peerIdParts)-1]
	id, err := peer.IDB58Decode(peerIdStr)
	if err != nil {
		return nil, err
	}

	return ipfsAddr{ma: m, id: id}, nil
}

func Transport(iaddr IPFSAddr) (maddr ma.Multiaddr) {
	maddr = iaddr.Multiaddr()

	// /ipfs/QmId is part of the transport address for p2p-circuit
	// TODO clean up the special case
	// we need a consistent way of composing and consumig multiaddrs
	// so that we don't have to do this
	_, err := maddr.ValueForProtocol(circuit.P_CIRCUIT)
	if err == nil {
		return maddr
	}

	split := ma.Split(maddr)
	maddr = ma.Join(split[:len(split)-1]...)
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cache

import (
	"sync"
	"time"

	"k8s.io/klog/v2"
	"k8s.io/utils/clock"
)

// ExpirationCache implements the store interface
//  1. All entries are automatically time stamped on insert
//     a. The key is computed based off the original item/keyFunc
//     b. The value inserted under that key is the timestamped item
//  2. Expiration happens lazily on read based on the expiration policy
//     a. No item can be inserted into the store while we're expiring
//     *any* item in the cache.
//  3. Time-stamps are stripped off unexpired entries before return
//
// Note that the ExpirationCache is inherently slower than a normal
// threadSafeStore because it takes a write lock every time it checks if
// an item has expired.
type ExpirationCache struct {
	cacheStorage     ThreadSafeStore
	keyFunc          KeyFunc
	clock            clock.Clock
	expirationPolicy ExpirationPolicy
	// expirationLock is a write lock used to guarantee that we don't clobber
	// newly inserted objects because of a stale expiration timestamp comparison
	expirationLock sync.Mutex
}

// ExpirationPolicy dictates when an object expires. Currently only abstracted out
// so unittests don't rely on the system clock.
type ExpirationPolicy interface {
	IsExpired(obj *TimestampedEntry) bool
}

// TTLPolicy implements a ttl based ExpirationPolicy.
type TTLPolicy struct {
	//	 >0: Expire entries with an age > ttl
	//	<=0: Don't expire any entry
	TTL time.Duration

	// Clock used to calculate ttl expiration
	Clock clock.Clock
}

// IsExpired returns true if the given object is older than the ttl, or it can't
// determine its age.
func (p *TTLPolicy) IsExpired(obj *TimestampedEntry) bool {
	return p.TTL > 0 && p.Clock.Since(obj.Timestamp) > p.TTL
}

// TimestampedEntry is the only type allowed in a ExpirationCache.
// Keep in mind that it is not safe to share timestamps between computers.
// Behavior may be inconsistent if you get a timestamp from the API Server and
// use it on the client machine as part of your ExpirationCache.
type TimestampedEntry struct {
	Obj       interface{}
	Timestamp time.Time
	key       string
}

// getTimestampedEntry returns the TimestampedEntry stored under the given key.
func (c *ExpirationCache) getTimestampedEntry(key string) (*TimestampedEntry, bool) {
	item, _ := c.cacheStorage.Get(key)
	if tsEntry, ok := item.(*TimestampedEntry); ok {
		return tsEntry, true
	}
	return nil, false
}

// getOrExpire retrieves the object from the TimestampedEntry if and only if it hasn't
// already expired. It holds a write lock across deletion.
func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) {
	// Prevent all inserts from the time we deem an item as "expired" to when we
	// delete it, so an un-expired item doesn't sneak in under the same key, just
	// before the Delete.
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	timestampedItem, exists := c.getTimestampedEntry(key)
	if !exists {
		return nil, false
	}
	if c.expirationPolicy.IsExpired(timestampedItem) {
		klog.V(4).Infof("Entry %v: %+v has expired", key, timestampedItem.Obj)
		c.cacheStorage.Delete(key)
		return nil, false
	}
	return timestampedItem.Obj, true
}

// GetByKey returns the item stored under the key, or sets exists=false.
func (c *ExpirationCache) GetByKey(key string) (interface{}, bool, error) {
	obj, exists := c.getOrExpire(key)
	return obj, exists, nil
}

// Get returns unexpired items. It purges the cache of expired items in the
// process.
func (c *ExpirationCache) Get(obj interface{}) (interface{}, bool, error) {
	key, err := c.keyFunc(obj)
	if err != nil {
		return nil, false, KeyError{obj, err}
	}
	obj, exists := c.getOrExpire(key)
	return obj, exists, nil
}

// List retrieves a list of unexpired items. It purges the cache of expired
// items in the process.
func (c *ExpirationCache) List() []interface{} {
	items := c.cacheStorage.List()

	list := make([]interface{}, 0, len(items))
	for _, item := range items {
		key := item.(*TimestampedEntry).key
		if obj, exists := c.getOrExpire(key); exists {
			list = append(list, obj)
		}
	}
	return list
}

// ListKeys returns a list of all keys in the expiration cache.
func (c *ExpirationCache) ListKeys() []string {
	return c.cacheStorage.ListKeys()
}

// Add timestamps an item and inserts it into the cache, overwriting entries
// that might exist under the same key.
func (c *ExpirationCache) Add(obj interface{}) error {
	key, err := c.keyFunc(obj)
	if err != nil {
		return KeyError{obj, err}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()

	c.cacheStorage.Add(key, &TimestampedEntry{obj, c.clock.Now(), key})
	return nil
}

// Update has not been implemented yet for lack of a use case, so this method
// simply calls `Add`. This effectively refreshes the timestamp.
func (c *ExpirationCache) Update(obj interface{}) error {
	return c.Add(obj)
}

// Delete removes an item from the cache.
func (c *ExpirationCache) Delete(obj interface{}) error {
	key, err := c.keyFunc(obj)
	if err != nil {
		return KeyError{obj, err}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	c.cacheStorage.Delete(key)
	return nil
}

// Replace will convert all items in the given list to TimestampedEntries
// before attempting the replace operation. The replace operation will
// delete the contents of the ExpirationCache `c`.
func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error {
	items := make(map[string]interface{}, len(list))
	ts := c.clock.Now()
	for _, item := range list {
		key, err := c.keyFunc(item)
		if err != nil {
			return KeyError{item, err}
		}
		items[key] = &TimestampedEntry{item, ts, key}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	c.cacheStorage.Replace(items, resourceVersion)
	return nil
}

// Resync is a no-op for one of these
func (c *ExpirationCache) Resync() error {
	return nil
}

// NewTTLStore creates and returns a ExpirationCache with a TTLPolicy
func NewTTLStore(keyFunc KeyFunc, ttl time.Duration) Store {
	return NewExpirationStore(keyFunc, &TTLPolicy{ttl, clock.RealClock{}})
}

// NewExpirationStore creates and returns a ExpirationCache for a given policy
func NewExpirationStore(keyFunc KeyFunc, expirationPolicy ExpirationPolicy) Store {
	return &ExpirationCache{
		cacheStorage:     NewThreadSafeStore(Indexers{}, Indices{}),
		keyFunc:          keyFunc,
		clock:            clock.RealClock{},
		expirationPolicy: expirationPolicy,
	}
}
<commit_msg>Remove log line from expiration cache<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cache

import (
	"sync"
	"time"

	"k8s.io/utils/clock"
)

// ExpirationCache implements the store interface
//  1. All entries are automatically time stamped on insert
//     a. The key is computed based off the original item/keyFunc
//     b. The value inserted under that key is the timestamped item
//  2. Expiration happens lazily on read based on the expiration policy
//     a. No item can be inserted into the store while we're expiring
//     *any* item in the cache.
//  3. Time-stamps are stripped off unexpired entries before return
//
// Note that the ExpirationCache is inherently slower than a normal
// threadSafeStore because it takes a write lock every time it checks if
// an item has expired.
type ExpirationCache struct {
	cacheStorage     ThreadSafeStore
	keyFunc          KeyFunc
	clock            clock.Clock
	expirationPolicy ExpirationPolicy
	// expirationLock is a write lock used to guarantee that we don't clobber
	// newly inserted objects because of a stale expiration timestamp comparison
	expirationLock sync.Mutex
}

// ExpirationPolicy dictates when an object expires. Currently only abstracted out
// so unittests don't rely on the system clock.
type ExpirationPolicy interface {
	IsExpired(obj *TimestampedEntry) bool
}

// TTLPolicy implements a ttl based ExpirationPolicy.
type TTLPolicy struct {
	//	 >0: Expire entries with an age > ttl
	//	<=0: Don't expire any entry
	TTL time.Duration

	// Clock used to calculate ttl expiration
	Clock clock.Clock
}

// IsExpired returns true if the given object is older than the ttl, or it can't
// determine its age.
func (p *TTLPolicy) IsExpired(obj *TimestampedEntry) bool {
	return p.TTL > 0 && p.Clock.Since(obj.Timestamp) > p.TTL
}

// TimestampedEntry is the only type allowed in a ExpirationCache.
// Keep in mind that it is not safe to share timestamps between computers.
// Behavior may be inconsistent if you get a timestamp from the API Server and
// use it on the client machine as part of your ExpirationCache.
type TimestampedEntry struct {
	Obj       interface{}
	Timestamp time.Time
	key       string
}

// getTimestampedEntry returns the TimestampedEntry stored under the given key.
func (c *ExpirationCache) getTimestampedEntry(key string) (*TimestampedEntry, bool) {
	item, _ := c.cacheStorage.Get(key)
	if tsEntry, ok := item.(*TimestampedEntry); ok {
		return tsEntry, true
	}
	return nil, false
}

// getOrExpire retrieves the object from the TimestampedEntry if and only if it hasn't
// already expired. It holds a write lock across deletion.
func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) {
	// Prevent all inserts from the time we deem an item as "expired" to when we
	// delete it, so an un-expired item doesn't sneak in under the same key, just
	// before the Delete.
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	timestampedItem, exists := c.getTimestampedEntry(key)
	if !exists {
		return nil, false
	}
	if c.expirationPolicy.IsExpired(timestampedItem) {
		c.cacheStorage.Delete(key)
		return nil, false
	}
	return timestampedItem.Obj, true
}

// GetByKey returns the item stored under the key, or sets exists=false.
func (c *ExpirationCache) GetByKey(key string) (interface{}, bool, error) {
	obj, exists := c.getOrExpire(key)
	return obj, exists, nil
}

// Get returns unexpired items. It purges the cache of expired items in the
// process.
func (c *ExpirationCache) Get(obj interface{}) (interface{}, bool, error) {
	key, err := c.keyFunc(obj)
	if err != nil {
		return nil, false, KeyError{obj, err}
	}
	obj, exists := c.getOrExpire(key)
	return obj, exists, nil
}

// List retrieves a list of unexpired items. It purges the cache of expired
// items in the process.
func (c *ExpirationCache) List() []interface{} {
	items := c.cacheStorage.List()

	list := make([]interface{}, 0, len(items))
	for _, item := range items {
		key := item.(*TimestampedEntry).key
		if obj, exists := c.getOrExpire(key); exists {
			list = append(list, obj)
		}
	}
	return list
}

// ListKeys returns a list of all keys in the expiration cache.
func (c *ExpirationCache) ListKeys() []string {
	return c.cacheStorage.ListKeys()
}

// Add timestamps an item and inserts it into the cache, overwriting entries
// that might exist under the same key.
func (c *ExpirationCache) Add(obj interface{}) error {
	key, err := c.keyFunc(obj)
	if err != nil {
		return KeyError{obj, err}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()

	c.cacheStorage.Add(key, &TimestampedEntry{obj, c.clock.Now(), key})
	return nil
}

// Update has not been implemented yet for lack of a use case, so this method
// simply calls `Add`. This effectively refreshes the timestamp.
func (c *ExpirationCache) Update(obj interface{}) error {
	return c.Add(obj)
}

// Delete removes an item from the cache.
func (c *ExpirationCache) Delete(obj interface{}) error {
	key, err := c.keyFunc(obj)
	if err != nil {
		return KeyError{obj, err}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	c.cacheStorage.Delete(key)
	return nil
}

// Replace will convert all items in the given list to TimestampedEntries
// before attempting the replace operation. The replace operation will
// delete the contents of the ExpirationCache `c`.
func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error {
	items := make(map[string]interface{}, len(list))
	ts := c.clock.Now()
	for _, item := range list {
		key, err := c.keyFunc(item)
		if err != nil {
			return KeyError{item, err}
		}
		items[key] = &TimestampedEntry{item, ts, key}
	}
	c.expirationLock.Lock()
	defer c.expirationLock.Unlock()
	c.cacheStorage.Replace(items, resourceVersion)
	return nil
}

// Resync is a no-op for one of these
func (c *ExpirationCache) Resync() error {
	return nil
}

// NewTTLStore creates and returns a ExpirationCache with a TTLPolicy
func NewTTLStore(keyFunc KeyFunc, ttl time.Duration) Store {
	return NewExpirationStore(keyFunc, &TTLPolicy{ttl, clock.RealClock{}})
}

// NewExpirationStore creates and returns a ExpirationCache for a given policy
func NewExpirationStore(keyFunc KeyFunc, expirationPolicy ExpirationPolicy) Store {
	return &ExpirationCache{
		cacheStorage:     NewThreadSafeStore(Indexers{}, Indices{}),
		keyFunc:          keyFunc,
		clock:            clock.RealClock{},
		expirationPolicy: expirationPolicy,
	}
}
<|endoftext|>


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

package test

import (
	"path/filepath"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/runutil"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

const (
	defaultProjectTestTimeout = 5 * time.Minute
)

// runProjectTest is a helper for running project tests.
func runProjectTest(ctx *tool.Context, testName, projectName, target string, env map[string]string, profiles []string) (_ *test.Result, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, profiles)
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to project directory.
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "projects", projectName)
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean.
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}

	// Set environment from the env argument map.
	opts := ctx.Run().Opts()
	for k, v := range env {
		opts.Env[k] = v
	}

	// Run the tests.
	if err := ctx.Run().TimedCommandWithOpts(defaultProjectTestTimeout, opts, "make", target); err != nil {
		if err == runutil.CommandTimedOutErr {
			return &test.Result{
				Status:       test.TimedOut,
				TimeoutValue: defaultProjectTestTimeout,
			}, nil
		} else {
			return nil, internalTestError{err, "Make " + target}
		}
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumBrowserTest runs the tests for the Vanadium browser.
func vanadiumBrowserTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	env := map[string]string{
		"XUNIT_OUTPUT_FILE": xunit.ReportPath(testName),
	}
	return runProjectTest(ctx, testName, "browser", "test", env, []string{"web"})
}

// vanadiumChatShellTest runs the tests for the chat shell client.
func vanadiumChatShellTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "chat", "test-shell", nil, nil)
}

// vanadiumChatWebTest runs the tests for the chat web client.
func vanadiumChatWebTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "chat", "test-web", nil, []string{"web"})
}

// vanadiumPipe2BrowserTest runs the tests for pipe2browser.
func vanadiumPipe2BrowserTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "pipe2browser", "test", nil, []string{"web"})
}
<commit_msg>TBR v23/internal/test: Increasing timeout for projects since shutdown() has become slower. We can revert if/when https://github.com/vanadium/issues/issues/374 is resolved.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package test

import (
	"path/filepath"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/runutil"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
	"v.io/x/devtools/internal/xunit"
)

const (
	defaultProjectTestTimeout = 10 * time.Minute
)

// runProjectTest is a helper for running project tests.
func runProjectTest(ctx *tool.Context, testName, projectName, target string, env map[string]string, profiles []string) (_ *test.Result, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, profiles)
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to project directory.
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "projects", projectName)
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean.
	if err := ctx.Run().Command("make", "clean"); err != nil {
		return nil, err
	}

	// Set environment from the env argument map.
	opts := ctx.Run().Opts()
	for k, v := range env {
		opts.Env[k] = v
	}

	// Run the tests.
	if err := ctx.Run().TimedCommandWithOpts(defaultProjectTestTimeout, opts, "make", target); err != nil {
		if err == runutil.CommandTimedOutErr {
			return &test.Result{
				Status:       test.TimedOut,
				TimeoutValue: defaultProjectTestTimeout,
			}, nil
		} else {
			return nil, internalTestError{err, "Make " + target}
		}
	}

	return &test.Result{Status: test.Passed}, nil
}

// vanadiumBrowserTest runs the tests for the Vanadium browser.
func vanadiumBrowserTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	env := map[string]string{
		"XUNIT_OUTPUT_FILE": xunit.ReportPath(testName),
	}
	return runProjectTest(ctx, testName, "browser", "test", env, []string{"web"})
}

// vanadiumChatShellTest runs the tests for the chat shell client.
func vanadiumChatShellTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "chat", "test-shell", nil, nil)
}

// vanadiumChatWebTest runs the tests for the chat web client.
func vanadiumChatWebTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "chat", "test-web", nil, []string{"web"})
}

// vanadiumPipe2BrowserTest runs the tests for pipe2browser.
func vanadiumPipe2BrowserTest(ctx *tool.Context, testName string, _ ...Opt) (*test.Result, error) {
	return runProjectTest(ctx, testName, "pipe2browser", "test", nil, []string{"web"})
}
<|endoftext|>


<commit_before>package v1

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"zxq.co/ripple/rippleapi/common"
)

type setAllowedData struct {
	UserID  int `json:"user_id"`
	Allowed int `json:"allowed"`
}

// UserManageSetAllowedPOST allows to set the allowed status of an user.
func UserManageSetAllowedPOST(md common.MethodData) common.CodeMessager {
	var data setAllowedData
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.Allowed < 0 || data.Allowed > 2 {
		return common.SimpleResponse(400, "Allowed status must be between 0 and 2")
	}
	var banDatetime int64
	var privsSet string
	if data.Allowed == 0 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges & ~3)"
	} else {
		banDatetime = 0
		privsSet = "privileges = (privileges | 3)"
	}
	_, err := md.DB.Exec("UPDATE users SET "+privsSet+", ban_datetime = ? WHERE id = ?", banDatetime, data.UserID)
	if err != nil {
		md.Err(err)
		return Err500
	}
	rapLog(md, fmt.Sprintf("changed UserID:%d's allowed to %d. This was done using the API's terrible ManageSetAllowed.", data.UserID, data.Allowed))
	go fixPrivileges(data.UserID, md.DB)
	query := `
SELECT users.id, users.username, register_datetime, privileges,
	latest_activity, users_stats.username_aka,
	users_stats.country
FROM users
LEFT JOIN users_stats
ON users.id=users_stats.id
WHERE users.id=?
LIMIT 1`
	return userPutsSingle(md, md.DB.QueryRowx(query, data.UserID))
}

type userEditData struct {
	ID          int     `json:"id"`
	Username    *string `json:"username"`
	UsernameAKA *string `json:"username_aka"`
	//Privileges    *uint64      `json:"privileges"`
	Country       *string      `json:"country"`
	SilenceInfo   *silenceInfo `json:"silence_info"`
	ResetUserpage bool         `json:"reset_userpage"`
	//ResetAvatar bool `json:"reset_avatar"`
}

// UserEditPOST allows to edit an user's information.
func UserEditPOST(md common.MethodData) common.CodeMessager {
	var data userEditData
	if err := md.Unmarshal(&data); err != nil {
		fmt.Println(err)
		return ErrBadJSON
	}

	if data.ID == 0 {
		return common.SimpleResponse(404, "That user could not be found")
	}

	var prevUser struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&prevUser, "SELECT username, privileges FROM users WHERE id = ? LIMIT 1", data.ID)

	switch err {
	case nil: // carry on
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found")
	default:
		md.Err(err)
		return Err500
	}

	const initQuery = "UPDATE users SET\n"
	q := initQuery
	var args []interface{}

	// totally did not realise I had to update some fields in users_stats as well
	// and just copy pasting the above code by prefixing "stats" to every
	// variable
	const statsInitQuery = "UPDATE users_stats SET\n"
	statsQ := statsInitQuery
	var statsArgs []interface{}

	if common.UserPrivileges(prevUser.Privileges)&common.AdminPrivilegeManageUsers != 0 &&
		data.ID != md.User.UserID {
		return common.SimpleResponse(403, "Can't edit that user")
	}

	if data.Username != nil {
		if strings.Contains(*data.Username, " ") && strings.Contains(*data.Username, "_") {
			return common.SimpleResponse(400, "Mixed spaces and underscores")
		}
		if usernameAvailable(md, *data.Username, data.ID) {
			return common.SimpleResponse(409, "User with that username exists")
		}
		jsonData, _ := json.Marshal(struct {
			UserID      int    `json:"userID"`
			NewUsername string `json:"newUsername"`
		}{data.ID, *data.Username})
		md.R.Publish("peppy:change_username", string(jsonData))
		appendToUserNotes(md, "Username change: "+prevUser.Username+" -> "+*data.Username, data.ID)
	}
	if data.UsernameAKA != nil {
		statsQ += "username_aka = ?,\n"
		statsArgs = append(statsArgs, *data.UsernameAKA)
	}
	/*if data.Privileges != nil {
		q += "privileges = ?,\n"
		args = append(args, *data.Privileges)
		// UserNormal or UserPublic changed
		if *data.Privileges & 3 != 3 && *data.Privileges & 3 != prevUser.Privileges & 3 {
			q += "ban_datetime = ?"
			args = append(args, meme)
		}
		// https://zxq.co/ripple/old-frontend/src/master/inc/Do.php#L355 ?
		// should also check for AdminManagePrivileges
		// should also check out the code for CM restring/banning
	}*/
	if data.Country != nil {
		statsQ += "country = ?,\n"
		statsArgs = append(statsArgs, *data.Country)
		rapLog(md, fmt.Sprintf("has changed %s country to %s", prevUser.Username, *data.Country))
		appendToUserNotes(md, "country changed to "+*data.Country, data.ID)
	}
	if data.SilenceInfo != nil && md.User.UserPrivileges&common.AdminPrivilegeSilenceUsers != 0 {
		q += "silence_end = ?, silence_reason = ?,\n"
		args = append(args, time.Time(data.SilenceInfo.End).Unix(), data.SilenceInfo.Reason)
	}
	if data.ResetUserpage {
		statsQ += "userpage_content = '',\n"
	}

	if q != initQuery {
		q = q[:len(q)-2] + " WHERE id = ? LIMIT 1"
		args = append(args, data.ID)
		_, err = md.DB.Exec(q, args...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}
	if statsQ != statsInitQuery {
		statsQ = statsQ[:len(statsQ)-2] + " WHERE id = ? LIMIT 1"
		statsArgs = append(statsArgs, data.ID)
		_, err = md.DB.Exec(statsQ, statsArgs...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}

	rapLog(md, fmt.Sprintf("has updated user %s", prevUser.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func rapLog(md common.MethodData, message string) {
	ua := string(md.Ctx.UserAgent())
	if len(ua) > 20 {
		ua = ua[:20] + "…"
	}
	through := "API"
	if ua != "" {
		through += " (" + ua + ")"
	}

	_, err := md.DB.Exec("INSERT INTO rap_logs(userid, text, datetime, through) VALUES (?, ?, ?, ?)",
		md.User.UserID, message, time.Now().Unix(), through)
	if err != nil {
		md.Err(err)
	}
}

func appendToUserNotes(md common.MethodData, message string, user int) {
	message = "\n[" + time.Now().Format("2006-01-02 15:04:05") + "] API: " + message
	_, err := md.DB.Exec("UPDATE users SET notes = CONCAT(notes, ?) WHERE id = ?",
		message, user)
	if err != nil {
		md.Err(err)
	}
}

func usernameAvailable(md common.MethodData, u string, userID int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE username_safe = ? AND id != ?)", common.SafeUsername(u), userID).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}
<commit_msg>use coalesce rather than notes directly<commit_after>package v1

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"zxq.co/ripple/rippleapi/common"
)

type setAllowedData struct {
	UserID  int `json:"user_id"`
	Allowed int `json:"allowed"`
}

// UserManageSetAllowedPOST allows to set the allowed status of an user.
func UserManageSetAllowedPOST(md common.MethodData) common.CodeMessager {
	var data setAllowedData
	if err := md.Unmarshal(&data); err != nil {
		return ErrBadJSON
	}
	if data.Allowed < 0 || data.Allowed > 2 {
		return common.SimpleResponse(400, "Allowed status must be between 0 and 2")
	}
	var banDatetime int64
	var privsSet string
	if data.Allowed == 0 {
		banDatetime = time.Now().Unix()
		privsSet = "privileges = (privileges & ~3)"
	} else {
		banDatetime = 0
		privsSet = "privileges = (privileges | 3)"
	}
	_, err := md.DB.Exec("UPDATE users SET "+privsSet+", ban_datetime = ? WHERE id = ?", banDatetime, data.UserID)
	if err != nil {
		md.Err(err)
		return Err500
	}
	rapLog(md, fmt.Sprintf("changed UserID:%d's allowed to %d. This was done using the API's terrible ManageSetAllowed.", data.UserID, data.Allowed))
	go fixPrivileges(data.UserID, md.DB)
	query := `
SELECT users.id, users.username, register_datetime, privileges,
	latest_activity, users_stats.username_aka,
	users_stats.country
FROM users
LEFT JOIN users_stats
ON users.id=users_stats.id
WHERE users.id=?
LIMIT 1`
	return userPutsSingle(md, md.DB.QueryRowx(query, data.UserID))
}

type userEditData struct {
	ID          int     `json:"id"`
	Username    *string `json:"username"`
	UsernameAKA *string `json:"username_aka"`
	//Privileges    *uint64      `json:"privileges"`
	Country       *string      `json:"country"`
	SilenceInfo   *silenceInfo `json:"silence_info"`
	ResetUserpage bool         `json:"reset_userpage"`
	//ResetAvatar bool `json:"reset_avatar"`
}

// UserEditPOST allows to edit an user's information.
func UserEditPOST(md common.MethodData) common.CodeMessager {
	var data userEditData
	if err := md.Unmarshal(&data); err != nil {
		fmt.Println(err)
		return ErrBadJSON
	}

	if data.ID == 0 {
		return common.SimpleResponse(404, "That user could not be found")
	}

	var prevUser struct {
		Username   string
		Privileges uint64
	}
	err := md.DB.Get(&prevUser, "SELECT username, privileges FROM users WHERE id = ? LIMIT 1", data.ID)

	switch err {
	case nil: // carry on
	case sql.ErrNoRows:
		return common.SimpleResponse(404, "That user could not be found")
	default:
		md.Err(err)
		return Err500
	}

	const initQuery = "UPDATE users SET\n"
	q := initQuery
	var args []interface{}

	// totally did not realise I had to update some fields in users_stats as well
	// and just copy pasting the above code by prefixing "stats" to every
	// variable
	const statsInitQuery = "UPDATE users_stats SET\n"
	statsQ := statsInitQuery
	var statsArgs []interface{}

	if common.UserPrivileges(prevUser.Privileges)&common.AdminPrivilegeManageUsers != 0 &&
		data.ID != md.User.UserID {
		return common.SimpleResponse(403, "Can't edit that user")
	}

	if data.Username != nil {
		if strings.Contains(*data.Username, " ") && strings.Contains(*data.Username, "_") {
			return common.SimpleResponse(400, "Mixed spaces and underscores")
		}
		if usernameAvailable(md, *data.Username, data.ID) {
			return common.SimpleResponse(409, "User with that username exists")
		}
		jsonData, _ := json.Marshal(struct {
			UserID      int    `json:"userID"`
			NewUsername string `json:"newUsername"`
		}{data.ID, *data.Username})
		md.R.Publish("peppy:change_username", string(jsonData))
		appendToUserNotes(md, "Username change: "+prevUser.Username+" -> "+*data.Username, data.ID)
	}
	if data.UsernameAKA != nil {
		statsQ += "username_aka = ?,\n"
		statsArgs = append(statsArgs, *data.UsernameAKA)
	}
	/*if data.Privileges != nil {
		q += "privileges = ?,\n"
		args = append(args, *data.Privileges)
		// UserNormal or UserPublic changed
		if *data.Privileges & 3 != 3 && *data.Privileges & 3 != prevUser.Privileges & 3 {
			q += "ban_datetime = ?"
			args = append(args, meme)
		}
		// https://zxq.co/ripple/old-frontend/src/master/inc/Do.php#L355 ?
		// should also check for AdminManagePrivileges
		// should also check out the code for CM restring/banning
	}*/
	if data.Country != nil {
		statsQ += "country = ?,\n"
		statsArgs = append(statsArgs, *data.Country)
		rapLog(md, fmt.Sprintf("has changed %s country to %s", prevUser.Username, *data.Country))
		appendToUserNotes(md, "country changed to "+*data.Country, data.ID)
	}
	if data.SilenceInfo != nil && md.User.UserPrivileges&common.AdminPrivilegeSilenceUsers != 0 {
		q += "silence_end = ?, silence_reason = ?,\n"
		args = append(args, time.Time(data.SilenceInfo.End).Unix(), data.SilenceInfo.Reason)
	}
	if data.ResetUserpage {
		statsQ += "userpage_content = '',\n"
	}

	if q != initQuery {
		q = q[:len(q)-2] + " WHERE id = ? LIMIT 1"
		args = append(args, data.ID)
		_, err = md.DB.Exec(q, args...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}
	if statsQ != statsInitQuery {
		statsQ = statsQ[:len(statsQ)-2] + " WHERE id = ? LIMIT 1"
		statsArgs = append(statsArgs, data.ID)
		_, err = md.DB.Exec(statsQ, statsArgs...)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}

	rapLog(md, fmt.Sprintf("has updated user %s", prevUser.Username))

	return userPutsSingle(md, md.DB.QueryRowx(userFields+" WHERE users.id = ? LIMIT 1", data.ID))
}

func rapLog(md common.MethodData, message string) {
	ua := string(md.Ctx.UserAgent())
	if len(ua) > 20 {
		ua = ua[:20] + "…"
	}
	through := "API"
	if ua != "" {
		through += " (" + ua + ")"
	}

	_, err := md.DB.Exec("INSERT INTO rap_logs(userid, text, datetime, through) VALUES (?, ?, ?, ?)",
		md.User.UserID, message, time.Now().Unix(), through)
	if err != nil {
		md.Err(err)
	}
}

func appendToUserNotes(md common.MethodData, message string, user int) {
	message = "\n[" + time.Now().Format("2006-01-02 15:04:05") + "] API: " + message
	_, err := md.DB.Exec("UPDATE users SET notes = CONCAT(COALESCE(notes, ''), ?) WHERE id = ?",
		message, user)
	if err != nil {
		md.Err(err)
	}
}

func usernameAvailable(md common.MethodData, u string, userID int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE username_safe = ? AND id != ?)", common.SafeUsername(u), userID).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}
<|endoftext|>


<commit_before>/*
 Copyright 2018 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 	http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package spongeforgedl

import (
	"encoding/json"
	"errors"
	"github.com/pufferpanel/apufferi"
	"github.com/pufferpanel/pufferd/commons"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/environments/envs"
	"github.com/pufferpanel/pufferd/programs/operations/ops"
	"github.com/pufferpanel/pufferd/programs/operations/ops/impl/forgedl"
	"net/http"
	"os"
	"path"
)

const DownloadApiUrl = "https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads?type=stable&limit=1"
const RecommendedApiUrl = "https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads/recommended"

var client = &http.Client{}

type SpongeForgeDl struct {
	ReleaseType string
}

type download struct {
	Dependencies dependencies        `json:"dependencies"`
	Artifacts    map[string]artifact `json:"artifacts"`
}

type dependencies struct {
	Forge     string `json:"forge"`
	Minecraft string `json:"minecraft"`
}

type artifact struct {
	Url string `json:"url"`
}

func (op SpongeForgeDl) Run(env envs.Environment) error {
	var versionData download

	if op.ReleaseType == "latest" {
		response, err := client.Get(DownloadApiUrl)
		defer commons.CloseResponse(response)
		if err != nil {
			return err
		}

		var all []download
		err = json.NewDecoder(response.Body).Decode(&all)
		if err != nil {
			return err
		}
		err = response.Body.Close()
		if err != nil {
			return err
		}

		versionData = all[0]
	} else {
		response, err := client.Get(RecommendedApiUrl)
		defer commons.CloseResponse(response)

		if err != nil {
			return err
		}

		err = json.NewDecoder(response.Body).Decode(&versionData)
		if err != nil {
			return err
		}
		err = response.Body.Close()
		if err != nil {
			return err
		}
	}

	if versionData.Artifacts == nil || len(versionData.Artifacts) == 0 {
		return errors.New("no artifacts found to download")
	}

	//convert to a forge operation and have built-in process run this
	mapping := make(map[string]interface{})
	mapping["version"] = versionData.Dependencies.Minecraft + "-" + versionData.Dependencies.Forge
	mapping["target"] = "forge-installer.jar"
	forgeDlOp := forgedl.Factory.Create(ops.CreateOperation{OperationArgs: mapping})

	err := forgeDlOp.Run(env)
	if err != nil {
		return err
	}

	err = os.Mkdir(path.Join(env.GetRootDirectory(), "mods"), 0755)
	if err != nil && !os.IsExist(err) {
		return err
	}

	file, err := environments.DownloadViaMaven(versionData.Artifacts[""].Url, env)
	if err != nil {
		return err
	}

	//going to stick the spongeforge rename in, to assist with those modpacks
	err = apufferi.CopyFile(file, path.Join("mods", "_aspongeforge.jar"))
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Update to sponge dl methods<commit_after>/*
 Copyright 2018 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 	http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package spongeforgedl

import (
	"encoding/json"
	"errors"
	"github.com/pufferpanel/apufferi"
	"github.com/pufferpanel/pufferd/commons"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/environments/envs"
	"github.com/pufferpanel/pufferd/programs/operations/ops"
	"github.com/pufferpanel/pufferd/programs/operations/ops/impl/forgedl"
	"net/http"
	"os"
	"path"
	"strings"
)

const DownloadApiUrl = "https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads?type=stable&limit=1"
const RecommendedApiUrl = "https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads/recommended"

var client = &http.Client{}
type SpongeForgeDl struct {
	ReleaseType string
}

type download struct {
	Dependencies dependencies        `json:"dependencies"`
	Artifacts    map[string]artifact `json:"artifacts"`
}

type dependencies struct {
	Forge     string `json:"forge"`
	Minecraft string `json:"minecraft"`
}

type artifact struct {
	Url string `json:"url"`
}

func (op SpongeForgeDl) Run(env envs.Environment) error {
	var versionData download

	if op.ReleaseType == "latest" {
		response, err := client.Get(DownloadApiUrl)
		defer commons.CloseResponse(response)
		if err != nil {
			return err
		}

		var all []download
		err = json.NewDecoder(response.Body).Decode(&all)
		if err != nil {
			return err
		}
		err = response.Body.Close()
		if err != nil {
			return err
		}

		versionData = all[0]
	} else {
		response, err := client.Get(RecommendedApiUrl)
		defer commons.CloseResponse(response)

		if err != nil {
			return err
		}

		err = json.NewDecoder(response.Body).Decode(&versionData)
		if err != nil {
			return err
		}
		err = response.Body.Close()
		if err != nil {
			return err
		}
	}

	if versionData.Artifacts == nil || len(versionData.Artifacts) == 0 {
		return errors.New("no artifacts found to download")
	}

	//convert to a forge operation and have built-in process run this
	mapping := make(map[string]interface{})
	var version = versionData.Dependencies.Forge
	if !strings.HasPrefix(version, versionData.Dependencies.Minecraft) {
		version += versionData.Dependencies.Minecraft + "-" + version
	}

	mapping["version"] = version
	mapping["target"] = "forge-installer.jar"
	forgeDlOp := forgedl.Factory.Create(ops.CreateOperation{OperationArgs: mapping})

	err := forgeDlOp.Run(env)
	if err != nil {
		return err
	}

	err = os.Mkdir(path.Join(env.GetRootDirectory(), "mods"), 0755)
	if err != nil && !os.IsExist(err) {
		return err
	}

	file, err := environments.DownloadViaMaven(versionData.Artifacts[""].Url, env)
	if err != nil {
		return err
	}

	//going to stick the spongeforge rename in, to assist with those modpacks
	err = apufferi.CopyFile(file, path.Join("mods", "_aspongeforge.jar"))
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package symlink

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type SL struct {
	dst  string
	path string
}

var osMkdirAll = os.MkdirAll

func New(link, dst string) (*SL, error) {
	var err error
	if dst, err = dirAbsPath(dst); err != nil {
		return nil, err
	}
	exist, _, err := dirExists(dst)
	msgerr := ""
	if err != nil {
		msgerr = fmt.Sprintf("\rError: '%+v'", err)
	}
	if !exist {
		return nil, fmt.Errorf("Unknown destination '%s'%s", dst, msgerr)
	}

	src, _ := dirAbsPath(link)
	dir := filepath.Dir(filepath.Dir(src)) + string(filepath.Separator)
	fmt.Printf("src='%+v\ndir=%+v\ndst=%+v\n", src, dir, dst)
	var hasDir, hasSrc bool
	var dirTarget, srcTarget string
	if hasDir, dirTarget, err = dirExists(dir); err != nil {
		if strings.Contains(err.Error(), "The system cannot find the") == false {
			return nil, fmt.Errorf("Impossible to check/access link parent folder '%s':\n'%+v'", dir, err)
		}
	}
	if !hasDir {
		if err = osMkdirAll(dir, os.ModeDir); err != nil {
			return nil, fmt.Errorf("Impossible to create link parent folder '%s':\n'%+v'", dir, err)
		}
	} else if dirTarget != "" {
		// move folder to x.1 (or error?)
	}
	if hasSrc, srcTarget, err = dirExists(src); err != nil {
		if strings.Contains(err.Error(), "The system cannot find the") == false {
			return nil, fmt.Errorf("Impossible to check/access link'%s':\n'%+v'", src, err)
		}
	}
	if srcTarget != "" {
		// check if points already to dst. If not move or error
	} else if hasSrc {
		// move folder to xx.1
	}
	// do symlink
	return nil, nil
}

// a/b => c:\path\to\a\b\
func dirAbsPath(path string) (string, error) {
	path = filepath.FromSlash(path)
	var err error
	path, err = filepath.Abs(path)
	if err != nil {
		return "", err
	}
	sep := string(filepath.Separator)
	if strings.HasSuffix(path, sep) == false {
		path = path + sep
	}
	return path, nil
}

var osStat = os.Stat

func dirExists(path string) (bool, string, error) {
	fi, err := osStat(path)
	if fi == nil {
		return false, "", err
	}
	// sys := fi.Sys().(*syscall.Win32FileAttributeData)
	if err == nil {
		return true, "", nil
	}
	if strings.HasPrefix(err.Error(), "readlink ") == false {
		return false, "", err
	}
	// This is a symlink (JUNCTION on Windows)
	dir := filepath.Dir(path)
	base := filepath.Base(dir)
	dir = filepath.Dir(dir)
	sdir := ""
	if sdir, err = execcmd("dir", ".", dir); err != nil {
		return false, "", err
	}
	r := regexp.MustCompile(fmt.Sprintf(`(?m)<J[UO]NCTION>\s+%s\s+\[([^\]]+)\]\s*$`, base))
	n := r.FindAllStringSubmatch(sdir, -1)
	// fmt.Printf("n='%+v'\nr='%+v'\n", n, r)
	if len(n) == 1 {
		return true, n[0][1], nil
	}
	return false, "", fmt.Errorf("Unable to find junction symlink in parent dir '%s' for '%s'", dir, base)
}

func cmdRun(cmd *exec.Cmd) error {
	return cmd.Run()
}

var execRun = cmdRun

func execcmd(exe, cmd string, dir string) (string, error) {
	args := strings.Split(cmd, " ")
	args = append([]string{"/c", exe}, args...)
	c := exec.Command("cmd", args...)
	c.Dir = dir
	var bout bytes.Buffer
	c.Stdout = &bout
	var berr bytes.Buffer
	c.Stderr = &berr
	err := execRun(c)
	if err != nil {
		return bout.String(), fmt.Errorf("Unable to run '%s %s' in '%s': err '%s'\n'%s'", exe, cmd, dir, err.Error(), berr.String())
	} else if berr.String() != "" {
		return bout.String(), fmt.Errorf("Warning on run '%s %s' in '%s': '%s'", exe, cmd, dir, berr.String())
	}
	return bout.String(), nil
}
<commit_msg>symlink: refactor link variable name<commit_after>package symlink

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

type SL struct {
	dst  string
	path string
}

var osMkdirAll = os.MkdirAll

func New(link, dst string) (*SL, error) {
	var err error
	if dst, err = dirAbsPath(dst); err != nil {
		return nil, err
	}
	exist, _, err := dirExists(dst)
	msgerr := ""
	if err != nil {
		msgerr = fmt.Sprintf("\rError: '%+v'", err)
	}
	if !exist {
		return nil, fmt.Errorf("Unknown destination '%s'%s", dst, msgerr)
	}

	if link, err = dirAbsPath(link); err != nil {
		return nil, err
	}
	linkdir := filepath.Dir(filepath.Dir(link)) + string(filepath.Separator)
	fmt.Printf("link='%+v\nlinkdir=%+v\ndst=%+v\n", link, linkdir, dst)
	var hasLinkDir, hasLink bool
	var linkDirTarget, linkTarget string
	if hasLinkDir, linkDirTarget, err = dirExists(linkdir); err != nil {
		if strings.Contains(err.Error(), "The system cannot find the") == false {
			return nil, fmt.Errorf("Impossible to check/access link parent folder '%s':\n'%+v'", linkdir, err)
		}
	}
	if !hasLinkDir {
		if err = osMkdirAll(linkdir, os.ModeDir); err != nil {
			return nil, fmt.Errorf("Impossible to create link parent folder '%s':\n'%+v'", linkdir, err)
		}
	} else if linkDirTarget != "" {
		// TODO move
		// move folder to x.1 (or error?)
	}
	if hasLink, linkTarget, err = dirExists(link); err != nil {
		if strings.Contains(err.Error(), "The system cannot find the") == false {
			return nil, fmt.Errorf("Impossible to check/access link'%s':\n'%+v'", link, err)
		}
	}
	if linkTarget != "" {
		// check if points already to dst. If not move or error
	} else if hasLink {
		// move folder to xx.1
	}
	// do symlink
	return nil, nil
}

// a/b => c:\path\to\a\b\
func dirAbsPath(path string) (string, error) {
	path = filepath.FromSlash(path)
	var err error
	path, err = filepath.Abs(path)
	if err != nil {
		return "", err
	}
	sep := string(filepath.Separator)
	if strings.HasSuffix(path, sep) == false {
		path = path + sep
	}
	return path, nil
}

var osStat = os.Stat

func dirExists(path string) (bool, string, error) {
	fi, err := osStat(path)
	if fi == nil {
		return false, "", err
	}
	// sys := fi.Sys().(*syscall.Win32FileAttributeData)
	if err == nil {
		return true, "", nil
	}
	if strings.HasPrefix(err.Error(), "readlink ") == false {
		return false, "", err
	}
	// This is a symlink (JUNCTION on Windows)
	dir := filepath.Dir(path)
	base := filepath.Base(dir)
	dir = filepath.Dir(dir)
	sdir := ""
	if sdir, err = execcmd("dir", ".", dir); err != nil {
		return false, "", err
	}
	r := regexp.MustCompile(fmt.Sprintf(`(?m)<J[UO]NCTION>\s+%s\s+\[([^\]]+)\]\s*$`, base))
	n := r.FindAllStringSubmatch(sdir, -1)
	fmt.Printf("n='%+v'\nr='%+v'\n", n, r)
	if len(n) == 1 {
		return true, n[0][1], nil
	}
	return false, "", fmt.Errorf("Unable to find junction symlink in parent dir '%s' for '%s'", dir, base)
}

func cmdRun(cmd *exec.Cmd) error {
	return cmd.Run()
}

var execRun = cmdRun

func execcmd(exe, cmd string, dir string) (string, error) {
	args := strings.Split(cmd, " ")
	args = append([]string{"/c", exe}, args...)
	c := exec.Command("cmd", args...)
	c.Dir = dir
	var bout bytes.Buffer
	c.Stdout = &bout
	var berr bytes.Buffer
	c.Stderr = &berr
	err := execRun(c)
	if err != nil {
		return bout.String(), fmt.Errorf("Unable to run '%s %s' in '%s': err '%s'\n'%s'", exe, cmd, dir, err.Error(), berr.String())
	} else if berr.String() != "" {
		return bout.String(), fmt.Errorf("Warning on run '%s %s' in '%s': '%s'", exe, cmd, dir, berr.String())
	}
	return bout.String(), nil
}
<|endoftext|>


<commit_before>package request_handler

import (
	"bytes"
	"errors"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/twitchscience/aws_utils/environment"

	"github.com/cactus/go-statsd-client/statsd"
	"github.com/twitchscience/gologging/gologging"
	"github.com/twitchscience/spade_edge/uuid"
)

var (
	Assigner uuid.UUIDAssigner = uuid.StartUUIDAssigner(
		os.Getenv("HOST"),
		os.Getenv("CLOUD_CLUSTER"),
	)
	xDomainContents []byte = func() []byte {
		filename := os.Getenv("CROSS_DOMAIN_LOCATION")
		if filename == "" {
			filename = "../build/config/crossdomain.xml"
		}
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatalln("Cross domain file not found: ", err)
		}
		return b
	}()
	xarth              []byte = []byte("XARTH")
	xmlApplicationType        = mime.TypeByExtension(".xml")
	DataFlag           []byte = []byte("data=")
	isProd                    = environment.IsProd()
)

type SpadeEdgeLogger interface {
	Log(EventRecord)
	Close()
}

type SpadeHandler struct {
	StatLogger statsd.Statter
	EdgeLogger SpadeEdgeLogger
	Assigner   uuid.UUIDAssigner
}

type FileAuditLogger struct {
	AuditLogger *gologging.UploadLogger
	SpadeLogger *gologging.UploadLogger
}

func (a *FileAuditLogger) Close() {
	a.AuditLogger.Close()
	a.SpadeLogger.Close()
}

func (a *FileAuditLogger) Log(log EventRecord) {
	a.AuditLogger.Log(log.AuditTrail())
	a.SpadeLogger.Log(log.HttpRequest())
}

func getIpFromHeader(headerKey string, header http.Header) string {
	clientIp := header.Get(headerKey)
	if clientIp == "" {
		return clientIp
	}
	comma := strings.Index(clientIp, ",")
	if comma > -1 {
		clientIp = clientIp[:comma]
	}

	return clientIp
}

func extractDataQuery(rawQuery string) string {
	dataIdx := strings.Index(rawQuery, "data=") + 5
	if dataIdx < 5 {
		return ""
	}
	endOfData := strings.IndexRune(rawQuery[dataIdx:], '&')
	if endOfData < 0 {
		return rawQuery[dataIdx:]
	}
	return rawQuery[dataIdx : dataIdx+endOfData]
}

func (s *SpadeHandler) HandleSpadeRequests(r *http.Request, context *requestContext) int {
	statTimer := newTimerInstance()

	clientIp := getIpFromHeader(context.IpHeader, r.Header)
	if clientIp == "" {
		return http.StatusBadRequest
	}
	context.Timers["ip"] = statTimer.stopTiming()

	var data string

	switch r.Method {
	case "GET":
		// Get data from url
		queryString, err := url.QueryUnescape(r.URL.RawQuery)
		if err != nil {
			return http.StatusBadRequest
		}
		data = extractDataQuery(queryString)
		if data == "" {
			return http.StatusBadRequest
		}
	case "POST":
		// This supports one request per post...
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return http.StatusBadRequest
		}
		if bytes.Equal(b[:5], DataFlag) {
			b = b[5:]
		}
		data = string(b)
	default:
		return http.StatusBadRequest
	}

	context.Timers["data"] = statTimer.stopTiming()

	// // get event
	uuid := s.Assigner.Assign()
	context.Timers["uuid"] = statTimer.stopTiming()

	record := &Event{
		ReceivedAt: context.Now,
		ClientIp:   clientIp,
		UUID:       uuid,
		Data:       data,
		Version:    EVENT_VERSION,
	}

	s.EdgeLogger.Log(record)
	context.Timers["write"] = statTimer.stopTiming()

	return http.StatusNoContent
}

const (
	ipOverrideHeader = "X-Original-Ip"
	ipForwardHeader  = "X-Forwarded-For"
	badEndpoint      = "FourOhFour"
	nTimers          = 5
)

func getTimeStampFromHeader(r *http.Request) (time.Time, error) {
	timeStamp := r.Header.Get("X-ORIGINAL-MSEC")
	if timeStamp != "" {
		splitIdx := strings.Index(timeStamp, ".")
		if splitIdx > -1 {
			secs, err := strconv.ParseInt(timeStamp[:splitIdx], 10, 64)
			if err == nil {
				return time.Unix(secs, 0), nil
			}
		}
	}
	return time.Time{}, errors.New("could not process timestamp from header")
}

var allowedMethods = map[string]bool{
	"GET":  true,
	"POST": true,
}

func (s *SpadeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !allowedMethods[r.Method] {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	now := time.Now()
	var ts time.Time
	var err error
	ts = now
	// For integration time correction
	if !isProd {
		ts, err = getTimeStampFromHeader(r)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	//
	context := &requestContext{
		Now:      ts,
		Method:   r.Method,
		Endpoint: r.URL.Path,
		IpHeader: ipForwardHeader,
		Timers:   make(map[string]time.Duration, nTimers),
	}
	timer := newTimerInstance()
	context.setStatus(s.serve(w, r, context))
	context.Timers["http"] = timer.stopTiming()

	context.recordStats(s.StatLogger)
}

func (s *SpadeHandler) serve(w http.ResponseWriter, r *http.Request, context *requestContext) int {
	var status int
	switch r.URL.Path {
	case "/crossdomain.xml":
		w.Header().Add("Content-Type", xmlApplicationType)
		w.Write(xDomainContents)
		status = http.StatusOK
	case "/healthcheck":
		status = http.StatusOK
	case "/xarth":
		w.Write(xarth)
		status = http.StatusOK
	// Accepted tracking endpoints.
	case "/":
		status = s.HandleSpadeRequests(r, context)
	case "/track":
		status = s.HandleSpadeRequests(r, context)
	case "/track/":
		status = s.HandleSpadeRequests(r, context)
	// dont track everything else
	default:
		context.Endpoint = badEndpoint
		status = http.StatusNotFound
	}
	w.WriteHeader(status)
	return status
}
<commit_msg>fixed integration test issue due to gologging using a sprintf. Will make issue to fix that...<commit_after>package request_handler

import (
	"bytes"
	"errors"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/twitchscience/aws_utils/environment"

	"github.com/cactus/go-statsd-client/statsd"
	"github.com/twitchscience/gologging/gologging"
	"github.com/twitchscience/spade_edge/uuid"
)

var (
	Assigner uuid.UUIDAssigner = uuid.StartUUIDAssigner(
		os.Getenv("HOST"),
		os.Getenv("CLOUD_CLUSTER"),
	)
	xDomainContents []byte = func() []byte {
		filename := os.Getenv("CROSS_DOMAIN_LOCATION")
		if filename == "" {
			filename = "../build/config/crossdomain.xml"
		}
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatalln("Cross domain file not found: ", err)
		}
		return b
	}()
	xarth              []byte = []byte("XARTH")
	xmlApplicationType        = mime.TypeByExtension(".xml")
	DataFlag           []byte = []byte("data=")
	isProd                    = environment.IsProd()
)

type SpadeEdgeLogger interface {
	Log(EventRecord)
	Close()
}

type SpadeHandler struct {
	StatLogger statsd.Statter
	EdgeLogger SpadeEdgeLogger
	Assigner   uuid.UUIDAssigner
}

type FileAuditLogger struct {
	AuditLogger *gologging.UploadLogger
	SpadeLogger *gologging.UploadLogger
}

func (a *FileAuditLogger) Close() {
	a.AuditLogger.Close()
	a.SpadeLogger.Close()
}

func (a *FileAuditLogger) Log(log EventRecord) {
	a.AuditLogger.Log("%s", log.AuditTrail())
	a.SpadeLogger.Log("%s", log.HttpRequest())
}

func getIpFromHeader(headerKey string, header http.Header) string {
	clientIp := header.Get(headerKey)
	if clientIp == "" {
		return clientIp
	}
	comma := strings.Index(clientIp, ",")
	if comma > -1 {
		clientIp = clientIp[:comma]
	}

	return clientIp
}

func extractDataQuery(rawQuery string) string {
	dataIdx := strings.Index(rawQuery, "data=") + 5
	if dataIdx < 5 {
		return ""
	}
	endOfData := strings.IndexRune(rawQuery[dataIdx:], '&')
	if endOfData < 0 {
		return rawQuery[dataIdx:]
	}
	return rawQuery[dataIdx : dataIdx+endOfData]
}

func (s *SpadeHandler) HandleSpadeRequests(r *http.Request, context *requestContext) int {
	statTimer := newTimerInstance()

	clientIp := getIpFromHeader(context.IpHeader, r.Header)
	if clientIp == "" {
		return http.StatusBadRequest
	}
	context.Timers["ip"] = statTimer.stopTiming()

	var data string

	switch r.Method {
	case "GET":
		// Get data from url
		queryString, err := url.QueryUnescape(r.URL.RawQuery)
		if err != nil {
			return http.StatusBadRequest
		}
		data = extractDataQuery(queryString)
		if data == "" {
			return http.StatusBadRequest
		}
	case "POST":
		// This supports one request per post...
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return http.StatusBadRequest
		}
		if bytes.Equal(b[:5], DataFlag) {
			b = b[5:]
		}
		data = string(b)
	default:
		return http.StatusBadRequest
	}

	context.Timers["data"] = statTimer.stopTiming()

	// // get event
	uuid := s.Assigner.Assign()
	context.Timers["uuid"] = statTimer.stopTiming()

	record := &Event{
		ReceivedAt: context.Now,
		ClientIp:   clientIp,
		UUID:       uuid,
		Data:       data,
		Version:    EVENT_VERSION,
	}

	s.EdgeLogger.Log(record)
	context.Timers["write"] = statTimer.stopTiming()

	return http.StatusNoContent
}

const (
	ipOverrideHeader = "X-Original-Ip"
	ipForwardHeader  = "X-Forwarded-For"
	badEndpoint      = "FourOhFour"
	nTimers          = 5
)

func getTimeStampFromHeader(r *http.Request) (time.Time, error) {
	timeStamp := r.Header.Get("X-ORIGINAL-MSEC")
	if timeStamp != "" {
		splitIdx := strings.Index(timeStamp, ".")
		if splitIdx > -1 {
			secs, err := strconv.ParseInt(timeStamp[:splitIdx], 10, 64)
			if err == nil {
				return time.Unix(secs, 0), nil
			}
		}
	}
	return time.Time{}, errors.New("could not process timestamp from header")
}

var allowedMethods = map[string]bool{
	"GET":  true,
	"POST": true,
}

func (s *SpadeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !allowedMethods[r.Method] {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	now := time.Now()
	var ts time.Time
	var err error
	ts = now
	// For integration time correction
	if !isProd {
		ts, err = getTimeStampFromHeader(r)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	}
	//
	context := &requestContext{
		Now:      ts,
		Method:   r.Method,
		Endpoint: r.URL.Path,
		IpHeader: ipForwardHeader,
		Timers:   make(map[string]time.Duration, nTimers),
	}
	timer := newTimerInstance()
	context.setStatus(s.serve(w, r, context))
	context.Timers["http"] = timer.stopTiming()

	context.recordStats(s.StatLogger)
}

func (s *SpadeHandler) serve(w http.ResponseWriter, r *http.Request, context *requestContext) int {
	var status int
	switch r.URL.Path {
	case "/crossdomain.xml":
		w.Header().Add("Content-Type", xmlApplicationType)
		w.Write(xDomainContents)
		status = http.StatusOK
	case "/healthcheck":
		status = http.StatusOK
	case "/xarth":
		w.Write(xarth)
		status = http.StatusOK
	// Accepted tracking endpoints.
	case "/":
		status = s.HandleSpadeRequests(r, context)
	case "/track":
		status = s.HandleSpadeRequests(r, context)
	case "/track/":
		status = s.HandleSpadeRequests(r, context)
	// dont track everything else
	default:
		context.Endpoint = badEndpoint
		status = http.StatusNotFound
	}
	w.WriteHeader(status)
	return status
}
<|endoftext|>


<commit_before>package session

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"net/http"
	"net/url"
	"time"
)

var ErrNoSession = errors.New("no session")

type Session struct {
	Id     string
	Values map[string]interface{}
}

type SessionStorage interface {
	SessionInit(sid string) (*Session, error)
	SessionRead(sid string) (*Session, error)
	SessionUpdate(*Session) error
	SessionDestroy(sid string) error
}

type Manager struct {
	CookieName string
	Storage    SessionStorage
	LifeTime   time.Duration
}

var defaultSessionManager = &Manager{
	CookieName: "session",
	Storage:    NewMembachedSessionStorage("localhost:11211", 30*24*time.Hour),
	LifeTime:   30 * 24 * time.Hour,
}

func generateId() string {
	b := make([]byte, 32)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(b)
}

func DefaultSessionManager() *Manager {
	return defaultSessionManager
}

func newCookie(name string, id string, age time.Duration) *http.Cookie {
	return &http.Cookie{
		Name:     name,
		Value:    url.QueryEscape(id),
		Path:     "/",
		HttpOnly: true,
		MaxAge:   int(age),
	}
}

func (m *Manager) newSession(w http.ResponseWriter) (*Session, error) {
	sid := generateId()
	s, err := m.Storage.SessionInit(sid)
	if err != nil {
		return nil, err
	}
	cookie := newCookie(m.CookieName, sid, m.LifeTime)
	http.SetCookie(w, cookie)
	return s, nil
}

func expiredCookie(name string) *http.Cookie {
	return &http.Cookie{
		Name:     name,
		Path:     "/",
		HttpOnly: true,
		Expires:  time.Now(),
		MaxAge:   -1,
	}
}

func (m *Manager) StartSession(w http.ResponseWriter, r *http.Request) (*Session, error) {
	cookie, err := r.Cookie(m.CookieName)
	if err == http.ErrNoCookie || cookie.Value == "" {
		return m.newSession(w)
	} else if err != nil {
		return nil, err
	}

	sid, err := url.QueryUnescape(cookie.Value)
	if err != nil {
		return nil, err
	}
	s, err := m.Storage.SessionRead(sid)
	if err == ErrNoSession {
		return m.newSession(w)
	} else if err != nil {
		return nil, err
	}
	http.SetCookie(w, newCookie(m.CookieName, cookie.Value, m.LifeTime))
	return s, nil
}

func (m *Manager) DestroySession(w http.ResponseWriter, r *http.Request) error {
	http.SetCookie(w, expiredCookie(m.CookieName))
	cookie, err := r.Cookie(m.CookieName)
	if err == http.ErrNoCookie {
		return nil
	} else if err != nil {
		return err
	}

	err = m.Storage.SessionDestroy(cookie.Value)
	if err == ErrNoSession {
		return nil
	}
	return err
}
<commit_msg>change session id length<commit_after>package session

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"net/http"
	"net/url"
	"time"
)

var ErrNoSession = errors.New("no session")

type Session struct {
	Id     string
	Values map[string]interface{}
}

type SessionStorage interface {
	SessionInit(sid string) (*Session, error)
	SessionRead(sid string) (*Session, error)
	SessionUpdate(*Session) error
	SessionDestroy(sid string) error
}

type Manager struct {
	CookieName string
	Storage    SessionStorage
	LifeTime   time.Duration
}

var defaultSessionManager = &Manager{
	CookieName: "session",
	Storage:    NewMembachedSessionStorage("localhost:11211", 30*24*time.Hour),
	LifeTime:   30 * 24 * time.Hour,
}

func generateId() string {
	b := make([]byte, 36)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(b)
}

func DefaultSessionManager() *Manager {
	return defaultSessionManager
}

func newCookie(name string, id string, age time.Duration) *http.Cookie {
	return &http.Cookie{
		Name:     name,
		Value:    url.QueryEscape(id),
		Path:     "/",
		HttpOnly: true,
		MaxAge:   int(age),
	}
}

func (m *Manager) newSession(w http.ResponseWriter) (*Session, error) {
	sid := generateId()
	s, err := m.Storage.SessionInit(sid)
	if err != nil {
		return nil, err
	}
	cookie := newCookie(m.CookieName, sid, m.LifeTime)
	http.SetCookie(w, cookie)
	return s, nil
}

func expiredCookie(name string) *http.Cookie {
	return &http.Cookie{
		Name:     name,
		Path:     "/",
		HttpOnly: true,
		Expires:  time.Now(),
		MaxAge:   -1,
	}
}

func (m *Manager) StartSession(w http.ResponseWriter, r *http.Request) (*Session, error) {
	cookie, err := r.Cookie(m.CookieName)
	if err == http.ErrNoCookie || cookie.Value == "" {
		return m.newSession(w)
	} else if err != nil {
		return nil, err
	}

	sid, err := url.QueryUnescape(cookie.Value)
	if err != nil {
		return nil, err
	}
	s, err := m.Storage.SessionRead(sid)
	if err == ErrNoSession {
		return m.newSession(w)
	} else if err != nil {
		return nil, err
	}
	http.SetCookie(w, newCookie(m.CookieName, cookie.Value, m.LifeTime))
	return s, nil
}

func (m *Manager) DestroySession(w http.ResponseWriter, r *http.Request) error {
	http.SetCookie(w, expiredCookie(m.CookieName))
	cookie, err := r.Cookie(m.CookieName)
	if err == http.ErrNoCookie {
		return nil
	} else if err != nil {
		return err
	}

	err = m.Storage.SessionDestroy(cookie.Value)
	if err == ErrNoSession {
		return nil
	}
	return err
}
<|endoftext|>


<commit_before>package controller

import (
	"testing"
)

func Test_Request_ExtendSlices(t *testing.T) {
	testCases := []struct {
		Input    Request
		Expected Request
	}{
		// This test ensures that the request is not manipulated when no slice IDs
		// are given.
		{
			Input: Request{
				SliceIDs: []string{},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
		},

		// This test ensures that the request is extended when one slice ID is
		// given.
		{
			Input: Request{
				SliceIDs: []string{"1"},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{"1"},
				Units: []Unit{
					{
						Name:    "unit@1.service",
						Content: "some unit content",
					},
				},
			},
		},

		// This test ensures that the request is extended when arbitrary slice IDs
		// are given.
		{
			Input: Request{
				SliceIDs: []string{"3", "5", "foo"},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{"3", "5", "foo"},
				Units: []Unit{
					{
						Name:    "unit@3.service",
						Content: "some unit content",
					},
					{
						Name:    "unit@5.service",
						Content: "some unit content",
					},
					{
						Name:    "unit@foo.service",
						Content: "some unit content",
					},
				},
			},
		},
	}

	for _, testCase := range testCases {
		output, err := testCase.Input.ExtendSlices()
		if err != nil {
			t.Fatalf("Request.ExtendSlices returned error: %#v", err)
		}

		for i, outputUnit := range output.Units {
			if outputUnit.Name != testCase.Expected.Units[i].Name {
				t.Fatalf("output unit name '%s' is not equal to expected unit name '%s'", outputUnit.Name, testCase.Expected.Units[i].Name)
			}
		}
	}
}
<commit_msg>more controller tests<commit_after>package controller

import (
	"testing"
)

func Test_Request_ExtendSlices(t *testing.T) {
	testCases := []struct {
		Input    Request
		Expected Request
	}{
		// This test ensures that the request is not manipulated when no slice IDs
		// are given.
		{
			Input: Request{
				SliceIDs: []string{},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
		},

		// This test ensures that the request is extended when one slice ID is
		// given.
		{
			Input: Request{
				SliceIDs: []string{"1"},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{"1"},
				Units: []Unit{
					{
						Name:    "unit@1.service",
						Content: "some unit content",
					},
				},
			},
		},

		// This test ensures that the request is extended when multiple slice IDs
		// and multiple unit files are given.
		{
			Input: Request{
				SliceIDs: []string{"1", "2"},
				Units: []Unit{
					{
						Name:    "foo@.service",
						Content: "some foo content",
					},
					{
						Name:    "bar@.service",
						Content: "some bar content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{"1", "2"},
				Units: []Unit{
					{
						Name:    "foo@1.service",
						Content: "some foo content",
					},
					{
						Name:    "bar@1.service",
						Content: "some bar content",
					},
					{
						Name:    "foo@2.service",
						Content: "some foo content",
					},
					{
						Name:    "bar@2.service",
						Content: "some bar content",
					},
				},
			},
		},

		// This test ensures that the request is extended when arbitrary slice IDs
		// are given.
		{
			Input: Request{
				SliceIDs: []string{"3", "5", "foo"},
				Units: []Unit{
					{
						Name:    "unit@.service",
						Content: "some unit content",
					},
				},
			},
			Expected: Request{
				SliceIDs: []string{"3", "5", "foo"},
				Units: []Unit{
					{
						Name:    "unit@3.service",
						Content: "some unit content",
					},
					{
						Name:    "unit@5.service",
						Content: "some unit content",
					},
					{
						Name:    "unit@foo.service",
						Content: "some unit content",
					},
				},
			},
		},
	}

	for _, testCase := range testCases {
		output, err := testCase.Input.ExtendSlices()
		if err != nil {
			t.Fatalf("Request.ExtendSlices returned error: %#v", err)
		}

		for i, outputUnit := range output.Units {
			if outputUnit.Name != testCase.Expected.Units[i].Name {
				t.Fatalf("output unit name '%s' is not equal to expected unit name '%s'", outputUnit.Name, testCase.Expected.Units[i].Name)
			}
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"github.com/astaxie/beego"

	. "github.com/francoishill/goangi2/context"
	. "github.com/francoishill/goangi2/responses"
	. "github.com/francoishill/goangi2/utils/entityUtils"
	. "github.com/francoishill/goangi2/utils/errorUtils"
	. "github.com/francoishill/goangi2/utils/oauth2Utils"
)

type BaseController struct {
	beego.Controller

	*BaseAppContext
}

func (this *BaseController) Prepare() {
	defer this.RecoverPanicAndServerError_InControllerPrepare()

	this.Controller.Prepare()

	if DefaultBaseAppContext == nil {
		panic("Cannot use BaseController, DefaultBaseAppContext is nil")
	}

	this.BaseAppContext = DefaultBaseAppContext
}

func (this *BaseController) PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func (this *BaseController) ServerJson_ErrorText(errorMessage string) {
	jsonData := map[string]interface{}{
		"Success": false,
		"Error":   errorMessage,
	}
	this.Data["json"] = jsonData
	this.ServeJson()
}

func (this *BaseController) ServeJsonResponseObject(responseObject IRouterResponseObject) {
	this.Data["json"] = responseObject
	this.ServeJson()
}

func (this *BaseController) onAjaxRouterPanicRecovery(recoveryObj interface{}) {
	this.Ctx.Output.SetStatus(500)
	requestUrl := this.Ctx.Request.URL
	remoteAddress := this.Ctx.Request.RemoteAddr
	userAgent := this.Ctx.Input.UserAgent()

	userMessage := LogRouterError_And_ExtractUserMessage(
		this.BaseAppContext.Logger,
		"Controller error:",
		requestUrl,
		remoteAddress,
		this.Ctx.Input.Proxy(),
		userAgent,
		recoveryObj,
	)
	this.ServerJson_ErrorText(userMessage)
}

func (this *BaseController) RecoverPanicAndServerError() {
	if r := recover(); r != nil {
		this.Ctx.Output.SetStatus(500)
		this.onAjaxRouterPanicRecovery(r)
	}
}

func (this *BaseController) RecoverPanicAndServerError_InControllerPrepare() {
	if r := recover(); r != nil {
		this.Ctx.Output.SetStatus(500)
		//Serve the error as-is, otherwise the osin errors will
		switch e := r.(type) {
		case *OsinAuthorizeError:
			this.Data["json"] = e
			this.ServeJson()
		case string:
			this.ServerJson_ErrorText(e)
		case error:
			this.ServerJson_ErrorText(e.Error())
		default:
			this.ServerJson_ErrorText(fmt.Sprintf("%+v", r))
		}
		this.StopRun()
	}
}

func (this *BaseController) CreateDefaultRouterOrmContext(beginTransaction bool) *OrmContext {
	return CreateOrmContext(this.Logger, nil, nil, beginTransaction)
}
<commit_msg>Added BaseController method `ServerJson_SuccessText`.<commit_after>package controllers

import (
	"fmt"
	"github.com/astaxie/beego"

	. "github.com/francoishill/goangi2/context"
	. "github.com/francoishill/goangi2/responses"
	. "github.com/francoishill/goangi2/utils/entityUtils"
	. "github.com/francoishill/goangi2/utils/errorUtils"
	. "github.com/francoishill/goangi2/utils/oauth2Utils"
)

type BaseController struct {
	beego.Controller

	*BaseAppContext
}

func (this *BaseController) Prepare() {
	defer this.RecoverPanicAndServerError_InControllerPrepare()

	this.Controller.Prepare()

	if DefaultBaseAppContext == nil {
		panic("Cannot use BaseController, DefaultBaseAppContext is nil")
	}

	this.BaseAppContext = DefaultBaseAppContext
}

func (this *BaseController) PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func (this *BaseController) ServerJson_ErrorText(errorMessage string) {
	jsonData := map[string]interface{}{
		"Success": false,
		"Error":   errorMessage,
	}
	this.Data["json"] = jsonData
	this.ServeJson()
}

func (this *BaseController) ServerJson_SuccessText(successMessage string) {
	jsonData := map[string]interface{}{
		"Success": true,
	}
	if successMessage != "" {
		jsonData["Message"] = successMessage
	}
	this.Data["json"] = jsonData
	this.ServeJson()
}

func (this *BaseController) ServeJsonResponseObject(responseObject IRouterResponseObject) {
	this.Data["json"] = responseObject
	this.ServeJson()
}

func (this *BaseController) onAjaxRouterPanicRecovery(recoveryObj interface{}) {
	this.Ctx.Output.SetStatus(500)
	requestUrl := this.Ctx.Request.URL
	remoteAddress := this.Ctx.Request.RemoteAddr
	userAgent := this.Ctx.Input.UserAgent()

	userMessage := LogRouterError_And_ExtractUserMessage(
		this.BaseAppContext.Logger,
		"Controller error:",
		requestUrl,
		remoteAddress,
		this.Ctx.Input.Proxy(),
		userAgent,
		recoveryObj,
	)
	this.ServerJson_ErrorText(userMessage)
}

func (this *BaseController) RecoverPanicAndServerError() {
	if r := recover(); r != nil {
		this.Ctx.Output.SetStatus(500)
		this.onAjaxRouterPanicRecovery(r)
	}
}

func (this *BaseController) RecoverPanicAndServerError_InControllerPrepare() {
	if r := recover(); r != nil {
		this.Ctx.Output.SetStatus(500)
		//Serve the error as-is, otherwise the osin errors will
		switch e := r.(type) {
		case *OsinAuthorizeError:
			this.Data["json"] = e
			this.ServeJson()
		case string:
			this.ServerJson_ErrorText(e)
		case error:
			this.ServerJson_ErrorText(e.Error())
		default:
			this.ServerJson_ErrorText(fmt.Sprintf("%+v", r))
		}
		this.StopRun()
	}
}

func (this *BaseController) CreateDefaultRouterOrmContext(beginTransaction bool) *OrmContext {
	return CreateOrmContext(this.Logger, nil, nil, beginTransaction)
}
<|endoftext|>


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

package storage

import (
	"fmt"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/coordinator/client/zookeeper"
	"github.com/control-center/serviced/domain/host"
	"github.com/zenoss/glog"
)

// Server manages the exporting of a file system to clients.
type Server struct {
	host   *host.Host
	driver StorageDriver
}

// StorageDriver is an interface that storage subsystem must implement to be used
// by this packages Server implementation.
type StorageDriver interface {
	ExportPath() string
	SetClients(clients ...string)
	Sync() error
}

// NewServer returns a Server object to manage the exported file system
func NewServer(driver StorageDriver, host *host.Host) (*Server, error) {
	if len(driver.ExportPath()) < 9 {
		return nil, fmt.Errorf("export path can not be empty")
	}

	s := &Server{
		host:   host,
		driver: driver,
	}

	return s, nil
}

func (s *Server) Run(shutdown <-chan interface{}, conn client.Connection) error {
	node := &Node{
		Host:       *s.host,
		ExportPath: fmt.Sprintf("%s:%s", s.host.IPAddr, s.driver.ExportPath()),
	}

	leader := conn.NewLeader("/storage/leader", node)
	leaderW, err := leader.TakeLead()
	if err != zookeeper.ErrDeadlock && err != nil {
		glog.Errorf("Could not take storage lead: %s", err)
		return err
	}

	defer leader.ReleaseLead()

	for {
		clients, clientW, err := conn.ChildrenW("/storage/clients")
		if err != nil {
			glog.Errorf("Could not set up watch for storage clients: %s", err)
			return err
		}

		s.driver.SetClients(clients...)
		if err := s.driver.Sync(); err != nil {
			glog.Errorf("Error syncing driver: %s", err)
			return err
		}

		select {
		case e := <-clientW:
			glog.Info("storage.server: receieved event: %s", e)
		case <-leaderW:
			err := fmt.Errorf("storage.server: lost lead")
			return err
		case <-shutdown:
			return nil
		}
	}
}
<commit_msg>Initialize storage leader and client nodes on service startup<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
	"fmt"

	"github.com/control-center/serviced/coordinator/client"
	"github.com/control-center/serviced/coordinator/client/zookeeper"
	"github.com/control-center/serviced/domain/host"
	"github.com/zenoss/glog"
)

// Server manages the exporting of a file system to clients.
type Server struct {
	host   *host.Host
	driver StorageDriver
}

// StorageDriver is an interface that storage subsystem must implement to be used
// by this packages Server implementation.
type StorageDriver interface {
	ExportPath() string
	SetClients(clients ...string)
	Sync() error
}

// NewServer returns a Server object to manage the exported file system
func NewServer(driver StorageDriver, host *host.Host) (*Server, error) {
	if len(driver.ExportPath()) < 9 {
		return nil, fmt.Errorf("export path can not be empty")
	}

	s := &Server{
		host:   host,
		driver: driver,
	}

	return s, nil
}

func (s *Server) Run(shutdown <-chan interface{}, conn client.Connection) error {
	node := &Node{
		Host:       *s.host,
		ExportPath: fmt.Sprintf("%s:%s", s.host.IPAddr, s.driver.ExportPath()),
	}

	// Create the storage leader and client nodes
	if exists, _ := conn.Exists("/storage/leader"); !exists {
		conn.CreateDir("/storage/leader")
	}

	if exists, _ := conn.Exists("/storage/clients"); !exists {
		conn.CreateDir("/storage/clients")
	}

	leader := conn.NewLeader("/storage/leader", node)
	leaderW, err := leader.TakeLead()
	if err != zookeeper.ErrDeadlock && err != nil {
		glog.Errorf("Could not take storage lead: %s", err)
		return err
	}

	defer leader.ReleaseLead()

	for {
		clients, clientW, err := conn.ChildrenW("/storage/clients")
		if err != nil {
			glog.Errorf("Could not set up watch for storage clients: %s", err)
			return err
		}

		s.driver.SetClients(clients...)
		if err := s.driver.Sync(); err != nil {
			glog.Errorf("Error syncing driver: %s", err)
			return err
		}

		select {
		case e := <-clientW:
			glog.Info("storage.server: receieved event: %s", e)
		case <-leaderW:
			err := fmt.Errorf("storage.server: lost lead")
			return err
		case <-shutdown:
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package index

import (
	"github.com/balzaczyy/golucene/core/util"
)

/*
IndexInput that knows how to read the byte slices written by Posting
and PostingVector. We read the bytes in each slice until we hit the
end of that slice at which point we read the forwarding address of
the next slice and then jump to it.
*/
type ByteSliceReader struct {
	*util.DataInputImpl
	pool         *util.ByteBlockPool
	bufferUpto   int
	buffer       []byte
	upto         int
	limit        int
	level        int
	bufferOffset int

	endIndex int
}

func (r *ByteSliceReader) init(pool *util.ByteBlockPool, startIndex, endIndex int) {
	assert(endIndex-startIndex >= 0)
	assert(startIndex >= 0)
	assert(endIndex >= 0)

	r.pool = pool
	r.endIndex = endIndex

	r.level = 0
	r.bufferUpto = startIndex / util.BYTE_BLOCK_SIZE
	r.bufferOffset = r.bufferUpto * util.BYTE_BLOCK_SIZE
	r.buffer = pool.Buffers[r.bufferUpto]
	r.upto = startIndex & util.BYTE_BLOCK_MASK

	firstSize := util.LEVEL_SIZE_ARRAY[0]

	if startIndex+firstSize >= endIndex {
		// there is only this one slice to read
		r.limit = endIndex & util.BYTE_BLOCK_MASK
	} else {
		r.limit = r.upto + firstSize - 4
	}
}

func (r *ByteSliceReader) eof() bool {
	panic("not implemented yet")
}
<commit_msg>implement ByteSliceReader.eof()<commit_after>package index

import (
	"github.com/balzaczyy/golucene/core/util"
)

/*
IndexInput that knows how to read the byte slices written by Posting
and PostingVector. We read the bytes in each slice until we hit the
end of that slice at which point we read the forwarding address of
the next slice and then jump to it.
*/
type ByteSliceReader struct {
	*util.DataInputImpl
	pool         *util.ByteBlockPool
	bufferUpto   int
	buffer       []byte
	upto         int
	limit        int
	level        int
	bufferOffset int

	endIndex int
}

func (r *ByteSliceReader) init(pool *util.ByteBlockPool, startIndex, endIndex int) {
	assert(endIndex-startIndex >= 0)
	assert(startIndex >= 0)
	assert(endIndex >= 0)

	r.pool = pool
	r.endIndex = endIndex

	r.level = 0
	r.bufferUpto = startIndex / util.BYTE_BLOCK_SIZE
	r.bufferOffset = r.bufferUpto * util.BYTE_BLOCK_SIZE
	r.buffer = pool.Buffers[r.bufferUpto]
	r.upto = startIndex & util.BYTE_BLOCK_MASK

	firstSize := util.LEVEL_SIZE_ARRAY[0]

	if startIndex+firstSize >= endIndex {
		// there is only this one slice to read
		r.limit = endIndex & util.BYTE_BLOCK_MASK
	} else {
		r.limit = r.upto + firstSize - 4
	}
}

func (r *ByteSliceReader) eof() bool {
	assert(r.upto+r.bufferOffset <= r.endIndex)
	return r.upto+r.bufferOffset == r.endIndex
}
<|endoftext|>