<commit_before>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// 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 service

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"sync/atomic"
	"time"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

type sequence struct {
	// The current position of the producer or consumer
	cursor,

	// The previous known position of the consumer (if producer) or producer (if consumer)
	gate,

	// These are fillers to pad the cache line, which is generally 64 bytes
	p2, p3, p4, p5, p6, p7 int64
}

func newSequence() *sequence {
	return &sequence{}
}

func (this *sequence) get() int64 {
	return atomic.LoadInt64(&this.cursor)
}

func (this *sequence) set(seq int64) {
	atomic.StoreInt64(&this.cursor, seq)
}

type buffer struct {
	id int64

	readIndex  int64 //读序号
	writeIndex int64 //写序号
	buf        []*[]byte

	size int64
	mask int64

	done int64

	pcond *sync.Cond
	ccond *sync.Cond
}

func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}

	if size == 0 {
		size = DefaultBufferSize
	}

	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		id:         atomic.AddInt64(&bufcnt, 1),
		readIndex:  int64(0),
		writeIndex: int64(0),
		buf:        make([]*[]byte, size),
		size:       size,
		mask:       size - 1,
		pcond:      sync.NewCond(new(sync.Mutex)),
		ccond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

func (this *buffer) ID() int64 {
	return this.id
}

func (this *buffer) Close() error {
	atomic.StoreInt64(&this.done, 1)

	this.pcond.L.Lock()
	this.ccond.Broadcast()
	this.pcond.L.Unlock()

	this.ccond.L.Lock()
	this.pcond.Broadcast()
	this.ccond.L.Unlock()

	return nil
}

/**
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() (p *[]byte, ok bool) {
	this.ccond.L.Lock()
	defer func() {
		this.pcond.Broadcast()
		this.ccond.L.Unlock()
		time.Sleep(5 * time.Microsecond)
	}()
	ok = false
	p = nil
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return nil, false
		}
		writeIndex = this.GetCurrentWriteIndex()
		if readIndex >= writeIndex {
			//fmt.Println("read wait")
			this.pcond.Broadcast()
			this.ccond.Wait()
		} else {
			break
		}
	}
	index := readIndex & this.mask //替代求模
	p = this.buf[index]
	this.buf[index] = nil
	atomic.AddInt64(&this.readIndex, int64(1))
	if p != nil {
		ok = true
	}
	return p, ok
}

/**
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in *[]byte) (ok bool) {
	this.pcond.L.Lock()
	defer func() {
		this.ccond.Broadcast()
		this.pcond.L.Unlock()
		time.Sleep(5 * time.Microsecond)
	}()
	ok = false
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return false
		}
		readIndex = this.GetCurrentReadIndex()
		if writeIndex >= readIndex && writeIndex-readIndex >= this.size {
			//fmt.Println("write wait")
			this.ccond.Broadcast()
			this.pcond.Wait()
			//time.Sleep(1 * time.Millisecond)
		} else {
			break
		}

	}
	index := writeIndex & this.mask //替代求模
	this.buf[index] = in
	atomic.AddInt64(&this.writeIndex, int64(1))
	ok = true
	return ok
}

/**
修改 尽量减少数据的创建
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()

	total := int64(0)

	for {
		time.Sleep(5 * time.Millisecond)
		if this.isDone() {
			return total, io.EOF
		}

		var write_bytes []byte

		b := make([]byte, 5)
		n, err := r.Read(b[0:1])
		if err != nil {
			return total, io.EOF
		}
		total += int64(n)
		max_cnt := 1
		for {
			if this.isDone() {
				return total, io.EOF
			}
			// If we have read 5 bytes and still not done, then there's a problem.
			if max_cnt > 4 {
				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
			}
			_, err := r.Read(b[max_cnt:(max_cnt + 1)])

			//fmt.Println(b)
			if err != nil {
				return total, err
			}
			if b[max_cnt] >= 0x80 {
				max_cnt++
			} else {
				break
			}
		}
		remlen, m := binary.Uvarint(b[1 : max_cnt+1])
		remlen_tmp := int64(remlen)
		start_ := int64(1) + int64(m)
		total_tmp := remlen_tmp + start_

		write_bytes = make([]byte, 0, total_tmp)
		write_bytes = append(write_bytes, b[0:m+1]...)
		nlen := int64(0)
		times := 0
		cnt_ := int64(32)
		for nlen < remlen_tmp {
			if this.isDone() {
				return total, io.EOF
			}
			if times > 100 {
				return total, io.EOF
			} else {
				times = 0
			}
			times++
			tmpm := remlen_tmp - nlen

			b_ := write_bytes[(start_ + nlen):]
			if tmpm > cnt_ {
				b_ = write_bytes[(start_ + nlen):(start_ + nlen + cnt_)]
			}

			//b_ := make([]byte, remlen)
			n, err = r.Read(b_[0:])

			if err != nil {
				/*Log.Errorc(func() string {
					return fmt.Sprintf("从conn读取数据失败(%s)(0)", err)
				})
				time.Sleep(5 * time.Millisecond)
				continue*/
				return total, err
			}
			//write_bytes = append(write_bytes, b_[0:]...)
			nlen += int64(n)
			total += int64(n)
		}

		ok := this.WriteBuffer(&write_bytes)

		if !ok {
			return total, errors.New("write ringbuffer failed")
		}
	}
}

//func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
//	defer this.Close()
//
//	total := int64(0)
//
//	for {
//		time.Sleep(5 * time.Millisecond)
//		if this.isDone() {
//			return total, io.EOF
//		}
//
//		var write_bytes []byte
//
//		b := make([]byte, 5)
//		n, err := r.Read(b[0:1])
//		if err != nil {
//			return total, io.EOF
//		}
//		total += int64(n)
//		max_cnt := 1
//		for {
//			if this.isDone() {
//				return total, io.EOF
//			}
//			// If we have read 5 bytes and still not done, then there's a problem.
//			if max_cnt > 4 {
//				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
//			}
//			_, err := r.Read(b[max_cnt:(max_cnt + 1)])
//
//			//fmt.Println(b)
//			if err != nil {
//				return total, err
//			}
//			if b[max_cnt] >= 0x80 {
//				max_cnt++
//			} else {
//				break
//			}
//		}
//		remlen, m := binary.Uvarint(b[1 : max_cnt + 1])
//		remlen_tmp := int64(remlen)
//		total_tmp := remlen_tmp + int64(1) + int64(m)
//
//		write_bytes = make([]byte, 0, total_tmp)
//		write_bytes = append(write_bytes, b[0:m + 1]...)
//		nlen := int64(0)
//		times := 0
//		cnt_ := 32
//		for nlen < remlen_tmp {
//			if this.isDone() {
//				return total, io.EOF
//			}
//			if times > 100 {
//				return total, io.EOF
//			} else {
//				times = 0
//			}
//			times++
//			tmpm := remlen_tmp - nlen
//
//			var b_ []byte
//			if tmpm < int64(cnt_) {
//				b_ = make([]byte, tmpm)
//			} else {
//				b_ = make([]byte, cnt_)
//			}
//
//			//b_ := make([]byte, remlen)
//			n, err = r.Read(b_[0:])
//
//			if err != nil {
//				/*Log.Errorc(func() string {
//					return fmt.Sprintf("从conn读取数据失败(%s)(0)", err)
//				})
//				time.Sleep(5 * time.Millisecond)
//				continue*/
//				return total, err
//			}
//			write_bytes = append(write_bytes, b_[0:]...)
//			nlen += int64(n)
//			total += int64(n)
//		}
//
//		ok := this.WriteBuffer(&write_bytes)
//
//		if !ok {
//			return total, errors.New("write ringbuffer failed")
//		}
//	}
//}

func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()

	total := int64(0)

	for {
		if this.isDone() {
			return total, io.EOF
		}

		p, ok := this.ReadBuffer()
		if !ok {
			return total, errors.New("read buffer failed")
		}
		// There's some data, let's process it first
		if len(*p) > 0 {
			n, err := w.Write(*p)
			total += int64(n)
			//Log.Debugc(func() string{ return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)})

			if err != nil {
				return total, err
			}
		}

	}
}

func (this *buffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}

func ringCopy(dst, src []byte, start int64) int {
	n := len(src)

	i, l := 0, 0

	for n > 0 {
		l = copy(dst[start:], src[i:])
		i += l
		n -= l

		if n > 0 {
			start = 0
		}
	}

	return i
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<commit_msg>修改 buffer.go process.go sendrecv.go buffer modify sleep 5 Microsecond modify buffre.go readfrom sleep 5毫秒 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<commit_after>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// 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 service

import (
	"bufio"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"sync/atomic"
	"time"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

type sequence struct {
	// The current position of the producer or consumer
	cursor,

	// The previous known position of the consumer (if producer) or producer (if consumer)
	gate,

	// These are fillers to pad the cache line, which is generally 64 bytes
	p2, p3, p4, p5, p6, p7 int64
}

func newSequence() *sequence {
	return &sequence{}
}

func (this *sequence) get() int64 {
	return atomic.LoadInt64(&this.cursor)
}

func (this *sequence) set(seq int64) {
	atomic.StoreInt64(&this.cursor, seq)
}

type buffer struct {
	id int64

	readIndex  int64 //读序号
	writeIndex int64 //写序号
	buf        []*[]byte

	size int64
	mask int64

	done int64

	pcond *sync.Cond
	ccond *sync.Cond
}

func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}

	if size == 0 {
		size = DefaultBufferSize
	}

	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		id:         atomic.AddInt64(&bufcnt, 1),
		readIndex:  int64(0),
		writeIndex: int64(0),
		buf:        make([]*[]byte, size),
		size:       size,
		mask:       size - 1,
		pcond:      sync.NewCond(new(sync.Mutex)),
		ccond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

func (this *buffer) ID() int64 {
	return this.id
}

func (this *buffer) Close() error {
	atomic.StoreInt64(&this.done, 1)

	this.pcond.L.Lock()
	this.ccond.Broadcast()
	this.pcond.L.Unlock()

	this.ccond.L.Lock()
	this.pcond.Broadcast()
	this.ccond.L.Unlock()

	return nil
}

/**
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() (p *[]byte, ok bool) {
	this.ccond.L.Lock()
	defer func() {
		this.pcond.Broadcast()
		this.ccond.L.Unlock()
		time.Sleep(5 * time.Microsecond)
	}()
	ok = false
	p = nil
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return nil, false
		}
		writeIndex = this.GetCurrentWriteIndex()
		if readIndex >= writeIndex {
			//fmt.Println("read wait")
			this.pcond.Broadcast()
			this.ccond.Wait()
		} else {
			break
		}
	}
	index := readIndex & this.mask //替代求模
	p = this.buf[index]
	this.buf[index] = nil
	atomic.AddInt64(&this.readIndex, int64(1))
	if p != nil {
		ok = true
	}
	return p, ok
}

/**
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in *[]byte) (ok bool) {
	this.pcond.L.Lock()
	defer func() {
		this.ccond.Broadcast()
		this.pcond.L.Unlock()
		time.Sleep(5 * time.Microsecond)
	}()
	ok = false
	readIndex := this.GetCurrentReadIndex()
	writeIndex := this.GetCurrentWriteIndex()
	for {
		if this.isDone() {
			return false
		}
		readIndex = this.GetCurrentReadIndex()
		if writeIndex >= readIndex && writeIndex-readIndex >= this.size {
			//fmt.Println("write wait")
			this.ccond.Broadcast()
			this.pcond.Wait()
			//time.Sleep(1 * time.Millisecond)
		} else {
			break
		}

	}
	index := writeIndex & this.mask //替代求模
	this.buf[index] = in
	atomic.AddInt64(&this.writeIndex, int64(1))
	ok = true
	return ok
}

/**
修改 尽量减少数据的创建
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()

	total := int64(0)

	for {
		time.Sleep(5 * time.Millisecond)
		if this.isDone() {
			return total, io.EOF
		}

		var write_bytes []byte

		b := make([]byte, 5)
		n, err := r.Read(b[0:1])
		if err != nil {
			return total, io.EOF
		}
		total += int64(n)
		max_cnt := 1
		for {
			if this.isDone() {
				return total, io.EOF
			}
			// If we have read 5 bytes and still not done, then there's a problem.
			if max_cnt > 4 {
				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
			}
			_, err := r.Read(b[max_cnt:(max_cnt + 1)])

			//fmt.Println(b)
			if err != nil {
				return total, err
			}
			if b[max_cnt] >= 0x80 {
				max_cnt++
			} else {
				break
			}
		}
		remlen, m := binary.Uvarint(b[1 : max_cnt+1])
		remlen_tmp := int64(remlen)
		start_ := int64(1) + int64(m)
		total_tmp := remlen_tmp + start_

		write_bytes = make([]byte, total_tmp)
		copy(write_bytes[0:m+1], b[0:m+1]...)
		nlen := int64(0)
		times := 0
		cnt_ := int64(32)
		for nlen < remlen_tmp {
			if this.isDone() {
				return total, io.EOF
			}
			if times > 100 {
				return total, io.EOF
			} else {
				times = 0
			}
			times++
			tmpm := remlen_tmp - nlen

			b_ := write_bytes[(start_ + nlen):]
			if tmpm > cnt_ {
				b_ = write_bytes[(start_ + nlen):(start_ + nlen + cnt_)]
			}

			//b_ := make([]byte, remlen)
			n, err = r.Read(b_[0:])

			if err != nil {
				/*Log.Errorc(func() string {
					return fmt.Sprintf("从conn读取数据失败(%s)(0)", err)
				})
				time.Sleep(5 * time.Millisecond)
				continue*/
				return total, err
			}
			//write_bytes = append(write_bytes, b_[0:]...)
			nlen += int64(n)
			total += int64(n)
		}

		ok := this.WriteBuffer(&write_bytes)

		if !ok {
			return total, errors.New("write ringbuffer failed")
		}
	}
}

//func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
//	defer this.Close()
//
//	total := int64(0)
//
//	for {
//		time.Sleep(5 * time.Millisecond)
//		if this.isDone() {
//			return total, io.EOF
//		}
//
//		var write_bytes []byte
//
//		b := make([]byte, 5)
//		n, err := r.Read(b[0:1])
//		if err != nil {
//			return total, io.EOF
//		}
//		total += int64(n)
//		max_cnt := 1
//		for {
//			if this.isDone() {
//				return total, io.EOF
//			}
//			// If we have read 5 bytes and still not done, then there's a problem.
//			if max_cnt > 4 {
//				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
//			}
//			_, err := r.Read(b[max_cnt:(max_cnt + 1)])
//
//			//fmt.Println(b)
//			if err != nil {
//				return total, err
//			}
//			if b[max_cnt] >= 0x80 {
//				max_cnt++
//			} else {
//				break
//			}
//		}
//		remlen, m := binary.Uvarint(b[1 : max_cnt + 1])
//		remlen_tmp := int64(remlen)
//		total_tmp := remlen_tmp + int64(1) + int64(m)
//
//		write_bytes = make([]byte, 0, total_tmp)
//		write_bytes = append(write_bytes, b[0:m + 1]...)
//		nlen := int64(0)
//		times := 0
//		cnt_ := 32
//		for nlen < remlen_tmp {
//			if this.isDone() {
//				return total, io.EOF
//			}
//			if times > 100 {
//				return total, io.EOF
//			} else {
//				times = 0
//			}
//			times++
//			tmpm := remlen_tmp - nlen
//
//			var b_ []byte
//			if tmpm < int64(cnt_) {
//				b_ = make([]byte, tmpm)
//			} else {
//				b_ = make([]byte, cnt_)
//			}
//
//			//b_ := make([]byte, remlen)
//			n, err = r.Read(b_[0:])
//
//			if err != nil {
//				/*Log.Errorc(func() string {
//					return fmt.Sprintf("从conn读取数据失败(%s)(0)", err)
//				})
//				time.Sleep(5 * time.Millisecond)
//				continue*/
//				return total, err
//			}
//			write_bytes = append(write_bytes, b_[0:]...)
//			nlen += int64(n)
//			total += int64(n)
//		}
//
//		ok := this.WriteBuffer(&write_bytes)
//
//		if !ok {
//			return total, errors.New("write ringbuffer failed")
//		}
//	}
//}

func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()

	total := int64(0)

	for {
		if this.isDone() {
			return total, io.EOF
		}

		p, ok := this.ReadBuffer()
		if !ok {
			return total, errors.New("read buffer failed")
		}
		// There's some data, let's process it first
		if len(*p) > 0 {
			n, err := w.Write(*p)
			total += int64(n)
			//Log.Debugc(func() string{ return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)})

			if err != nil {
				return total, err
			}
		}

	}
}

func (this *buffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}

func ringCopy(dst, src []byte, start int64) int {
	n := len(src)

	i, l := 0, 0

	for n > 0 {
		l = copy(dst[start:], src[i:])
		i += l
		n -= l

		if n > 0 {
			start = 0
		}
	}

	return i
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<|endoftext|>


<commit_before>package core

import (
	"crypto/sha256"
	"errors"
	ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr"
	"gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/OpenBazaar/jsonpb"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"golang.org/x/net/context"
)

// ModeratorPointerID  moderator ipfs multihash
var ModeratorPointerID multihash.Multihash

// ErrNoListings - no listing error
// FIXME : This is not used anywhere
var ErrNoListings = errors.New("no listings to set moderators on")

func init() {
	modHash := sha256.Sum256([]byte("moderators"))
	encoded, err := multihash.Encode(modHash[:], multihash.SHA2_256)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash encode)")
	}
	mh, err := multihash.Cast(encoded)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash cast)")
	}
	ModeratorPointerID = mh
}

// IsModerator - Am I a moderator?
func (n *OpenBazaarNode) IsModerator() bool {
	profile, err := n.GetProfile()
	if err != nil {
		return false
	}
	return profile.Moderator
}

// SetSelfAsModerator - set self as a moderator
func (n *OpenBazaarNode) SetSelfAsModerator(moderator *pb.Moderator) error {
	if moderator != nil {
		if moderator.Fee == nil {
			return errors.New("Moderator must have a fee set")
		}
		if (int(moderator.Fee.FeeType) == 0 || int(moderator.Fee.FeeType) == 2) && moderator.Fee.FixedFee == nil {
			return errors.New("Fixed fee must be set when using a fixed fee type")
		}

		// Update profile
		profile, err := n.GetProfile()
		if err != nil {
			return err
		}

		var currencies []string
		settingsData, _ := n.Datastore.Settings().Get()
		if settingsData.PreferredCurrencies != nil {
			currencies = append(currencies, *settingsData.PreferredCurrencies...)
		} else {
			for ct := range n.Multiwallet {
				currencies = append(currencies, ct.CurrencyCode())
			}
		}
		for _, cc := range currencies {
			moderator.AcceptedCurrencies = append(moderator.AcceptedCurrencies, NormalizeCurrencyCode(cc))
		}

		profile.Moderator = true
		profile.ModeratorInfo = moderator
		err = n.UpdateProfile(&profile)
		if err != nil {
			return err
		}
	}

	// Publish pointer
	pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MODERATOR)
	ctx := context.Background()
	if err != nil || len(pointers) == 0 {
		addr, err := ma.NewMultiaddr("/ipfs/" + n.IpfsNode.Identity.Pretty())
		if err != nil {
			return err
		}
		pointer, err := ipfs.NewPointer(ModeratorPointerID, 64, addr, []byte(n.IpfsNode.Identity.Pretty()))
		if err != nil {
			return err
		}
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointer)
		pointer.Purpose = ipfs.MODERATOR
		err = n.Datastore.Pointers().Put(pointer)
		if err != nil {
			return err
		}
	} else {
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointers[0])
	}
	return nil
}

// RemoveSelfAsModerator - relinquish moderatorship
func (n *OpenBazaarNode) RemoveSelfAsModerator() error {
	// Update profile
	profile, err := n.GetProfile()
	if err != nil {
		return err
	}
	profile.Moderator = false
	err = n.UpdateProfile(&profile)
	if err != nil {
		return err
	}

	// Delete pointer from database
	err = n.Datastore.Pointers().DeleteAll(ipfs.MODERATOR)
	if err != nil {
		return err
	}
	return nil
}

// GetModeratorFee - fetch moderator fee
func (n *OpenBazaarNode) GetModeratorFee(transactionTotal uint64, paymentCoin, currencyCode string) (uint64, error) {
	file, err := ioutil.ReadFile(path.Join(n.RepoPath, "root", "profile.json"))
	if err != nil {
		return 0, err
	}
	profile := new(pb.Profile)
	err = jsonpb.UnmarshalString(string(file), profile)
	if err != nil {
		return 0, err
	}

	switch profile.ModeratorInfo.Fee.FeeType {
	case pb.Moderator_Fee_PERCENTAGE:
		return uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100)), nil
	case pb.Moderator_Fee_FIXED:

		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			if profile.ModeratorInfo.Fee.FixedFee.Amount >= transactionTotal {
				return 0, errors.New("Fixed moderator fee exceeds transaction amount")
			}
			return profile.ModeratorInfo.Fee.FixedFee.Amount, nil
		}
		fee, err := n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
		if err != nil {
			return 0, err
		} else if fee >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fee, err

	case pb.Moderator_Fee_FIXED_PLUS_PERCENTAGE:
		var fixed uint64
		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			fixed = profile.ModeratorInfo.Fee.FixedFee.Amount
		} else {
			fixed, err = n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
			if err != nil {
				return 0, err
			}
		}
		percentage := uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100))
		if fixed+percentage >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fixed + percentage, nil
	default:
		return 0, errors.New("Unrecognized fee type")
	}
}

// SetCurrencyOnListings - set currencies accepted for a listing
func (n *OpenBazaarNode) SetCurrencyOnListings(currencies []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}

	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() && filepath.Ext(p) == ".json" {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}

			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}

			sl.Listing.Metadata.AcceptedCurrencies = currencies

			savedCoupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			for _, coupon := range sl.Listing.Coupons {
				for _, c := range savedCoupons {
					if coupon.GetHash() == c.Hash {
						coupon.Code = &pb.Listing_Coupon_DiscountCode{c.Code}
						break
					}
				}
			}
			if sl.Listing.Metadata != nil && sl.Listing.Metadata.Version == 1 {
				for _, so := range sl.Listing.ShippingOptions {
					for _, ser := range so.Services {
						ser.AdditionalItemPrice = ser.Price
					}
				}
			}

			inventory, err := n.Datastore.Inventory().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}

			// Build the inventory list
			for variant, count := range inventory {
				for i, s := range sl.Listing.Item.Skus {
					if variant == i {
						s.Quantity = count
						break
					}
				}
			}

			n.UpdateListing(sl.Listing, false)

		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	n.SeedNode()

	return nil
}

// SetModeratorsOnListings - set moderators for a listing
func (n *OpenBazaarNode) SetModeratorsOnListings(moderators []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}
	hashes := make(map[string]string)
	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}
			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}
			coupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			couponMap := make(map[string]string)
			for _, c := range coupons {
				couponMap[c.Hash] = c.Code
			}
			for _, coupon := range sl.Listing.Coupons {
				code, ok := couponMap[coupon.GetHash()]
				if ok {
					coupon.Code = &pb.Listing_Coupon_DiscountCode{DiscountCode: code}
				}
			}

			sl.Listing.Moderators = moderators
			sl, err = n.SignListing(sl.Listing)
			if err != nil {
				return err
			}
			m := jsonpb.Marshaler{
				EnumsAsInts:  false,
				EmitDefaults: false,
				Indent:       "    ",
				OrigName:     false,
			}
			fi, err := os.Create(p)
			if err != nil {
				return err
			}
			out, err := m.MarshalToString(sl)
			if err != nil {
				return err
			}
			if _, err := fi.WriteString(out); err != nil {
				return err
			}
			hash, err := ipfs.GetHashOfFile(n.IpfsNode, p)
			if err != nil {
				return err
			}
			hashes[sl.Listing.Slug] = hash

			return nil
		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	// Update moderators and hashes on index
	updater := func(listing *ListingData) error {
		listing.ModeratorIDs = moderators
		if hash, ok := hashes[listing.Slug]; ok {
			listing.Hash = hash
		}
		return nil
	}
	return n.UpdateEachListingOnIndex(updater)
}

// NotifyModerators - notify moderators(peers)
func (n *OpenBazaarNode) NotifyModerators(moderators []string) error {
	settings, err := n.Datastore.Settings().Get()
	if err != nil {
		return err
	}
	currentMods := make(map[string]bool)
	if settings.StoreModerators != nil {
		for _, mod := range *settings.StoreModerators {
			currentMods[mod] = true
		}
	}
	var addedMods []string
	for _, mod := range moderators {
		if !currentMods[mod] {
			addedMods = append(addedMods, mod)
		} else {
			delete(currentMods, mod)
		}
	}

	removedMods := currentMods

	for _, mod := range addedMods {
		go n.SendModeratorAdd(mod)
	}
	for mod := range removedMods {
		go n.SendModeratorRemove(mod)
	}
	return nil
}
<commit_msg>Migrate to listings.go<commit_after>package core

import (
	"crypto/sha256"
	"errors"
	ma "gx/ipfs/QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb/go-multiaddr"
	"gx/ipfs/QmZyZDi491cCNTLfAhwcaDii2Kg4pwKRkhqQzURGDvY6ua/go-multihash"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/OpenBazaar/jsonpb"
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/pb"
	"golang.org/x/net/context"
)

// ModeratorPointerID  moderator ipfs multihash
var ModeratorPointerID multihash.Multihash

// ErrNoListings - no listing error
// FIXME : This is not used anywhere
var ErrNoListings = errors.New("no listings to set moderators on")

func init() {
	modHash := sha256.Sum256([]byte("moderators"))
	encoded, err := multihash.Encode(modHash[:], multihash.SHA2_256)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash encode)")
	}
	mh, err := multihash.Cast(encoded)
	if err != nil {
		log.Fatal("Error creating moderator pointer ID (multihash cast)")
	}
	ModeratorPointerID = mh
}

// IsModerator - Am I a moderator?
func (n *OpenBazaarNode) IsModerator() bool {
	profile, err := n.GetProfile()
	if err != nil {
		return false
	}
	return profile.Moderator
}

// SetSelfAsModerator - set self as a moderator
func (n *OpenBazaarNode) SetSelfAsModerator(moderator *pb.Moderator) error {
	if moderator != nil {
		if moderator.Fee == nil {
			return errors.New("Moderator must have a fee set")
		}
		if (int(moderator.Fee.FeeType) == 0 || int(moderator.Fee.FeeType) == 2) && moderator.Fee.FixedFee == nil {
			return errors.New("Fixed fee must be set when using a fixed fee type")
		}

		// Update profile
		profile, err := n.GetProfile()
		if err != nil {
			return err
		}

		var currencies []string
		settingsData, _ := n.Datastore.Settings().Get()
		if settingsData.PreferredCurrencies != nil {
			currencies = append(currencies, *settingsData.PreferredCurrencies...)
		} else {
			for ct := range n.Multiwallet {
				currencies = append(currencies, ct.CurrencyCode())
			}
		}
		for _, cc := range currencies {
			moderator.AcceptedCurrencies = append(moderator.AcceptedCurrencies, NormalizeCurrencyCode(cc))
		}

		profile.Moderator = true
		profile.ModeratorInfo = moderator
		err = n.UpdateProfile(&profile)
		if err != nil {
			return err
		}
	}

	// Publish pointer
	pointers, err := n.Datastore.Pointers().GetByPurpose(ipfs.MODERATOR)
	ctx := context.Background()
	if err != nil || len(pointers) == 0 {
		addr, err := ma.NewMultiaddr("/ipfs/" + n.IpfsNode.Identity.Pretty())
		if err != nil {
			return err
		}
		pointer, err := ipfs.NewPointer(ModeratorPointerID, 64, addr, []byte(n.IpfsNode.Identity.Pretty()))
		if err != nil {
			return err
		}
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointer)
		pointer.Purpose = ipfs.MODERATOR
		err = n.Datastore.Pointers().Put(pointer)
		if err != nil {
			return err
		}
	} else {
		go ipfs.PublishPointer(n.IpfsNode, ctx, pointers[0])
	}
	return nil
}

// RemoveSelfAsModerator - relinquish moderatorship
func (n *OpenBazaarNode) RemoveSelfAsModerator() error {
	// Update profile
	profile, err := n.GetProfile()
	if err != nil {
		return err
	}
	profile.Moderator = false
	err = n.UpdateProfile(&profile)
	if err != nil {
		return err
	}

	// Delete pointer from database
	err = n.Datastore.Pointers().DeleteAll(ipfs.MODERATOR)
	if err != nil {
		return err
	}
	return nil
}

// GetModeratorFee - fetch moderator fee
func (n *OpenBazaarNode) GetModeratorFee(transactionTotal uint64, paymentCoin, currencyCode string) (uint64, error) {
	file, err := ioutil.ReadFile(path.Join(n.RepoPath, "root", "profile.json"))
	if err != nil {
		return 0, err
	}
	profile := new(pb.Profile)
	err = jsonpb.UnmarshalString(string(file), profile)
	if err != nil {
		return 0, err
	}

	switch profile.ModeratorInfo.Fee.FeeType {
	case pb.Moderator_Fee_PERCENTAGE:
		return uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100)), nil
	case pb.Moderator_Fee_FIXED:

		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			if profile.ModeratorInfo.Fee.FixedFee.Amount >= transactionTotal {
				return 0, errors.New("Fixed moderator fee exceeds transaction amount")
			}
			return profile.ModeratorInfo.Fee.FixedFee.Amount, nil
		}
		fee, err := n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
		if err != nil {
			return 0, err
		} else if fee >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fee, err

	case pb.Moderator_Fee_FIXED_PLUS_PERCENTAGE:
		var fixed uint64
		if NormalizeCurrencyCode(profile.ModeratorInfo.Fee.FixedFee.CurrencyCode) == NormalizeCurrencyCode(currencyCode) {
			fixed = profile.ModeratorInfo.Fee.FixedFee.Amount
		} else {
			fixed, err = n.getPriceInSatoshi(paymentCoin, profile.ModeratorInfo.Fee.FixedFee.CurrencyCode, profile.ModeratorInfo.Fee.FixedFee.Amount)
			if err != nil {
				return 0, err
			}
		}
		percentage := uint64(float64(transactionTotal) * (float64(profile.ModeratorInfo.Fee.Percentage) / 100))
		if fixed+percentage >= transactionTotal {
			return 0, errors.New("Fixed moderator fee exceeds transaction amount")
		}
		return fixed + percentage, nil
	default:
		return 0, errors.New("Unrecognized fee type")
	}
}

// SetModeratorsOnListings - set moderators for a listing
func (n *OpenBazaarNode) SetModeratorsOnListings(moderators []string) error {
	absPath, err := filepath.Abs(path.Join(n.RepoPath, "root", "listings"))
	if err != nil {
		return err
	}
	hashes := make(map[string]string)
	walkpath := func(p string, f os.FileInfo, err error) error {
		if !f.IsDir() {
			file, err := ioutil.ReadFile(p)
			if err != nil {
				return err
			}
			sl := new(pb.SignedListing)
			err = jsonpb.UnmarshalString(string(file), sl)
			if err != nil {
				return err
			}
			coupons, err := n.Datastore.Coupons().Get(sl.Listing.Slug)
			if err != nil {
				return err
			}
			couponMap := make(map[string]string)
			for _, c := range coupons {
				couponMap[c.Hash] = c.Code
			}
			for _, coupon := range sl.Listing.Coupons {
				code, ok := couponMap[coupon.GetHash()]
				if ok {
					coupon.Code = &pb.Listing_Coupon_DiscountCode{DiscountCode: code}
				}
			}

			sl.Listing.Moderators = moderators
			sl, err = n.SignListing(sl.Listing)
			if err != nil {
				return err
			}
			m := jsonpb.Marshaler{
				EnumsAsInts:  false,
				EmitDefaults: false,
				Indent:       "    ",
				OrigName:     false,
			}
			fi, err := os.Create(p)
			if err != nil {
				return err
			}
			out, err := m.MarshalToString(sl)
			if err != nil {
				return err
			}
			if _, err := fi.WriteString(out); err != nil {
				return err
			}
			hash, err := ipfs.GetHashOfFile(n.IpfsNode, p)
			if err != nil {
				return err
			}
			hashes[sl.Listing.Slug] = hash

			return nil
		}
		return nil
	}

	err = filepath.Walk(absPath, walkpath)
	if err != nil {
		return err
	}

	// Update moderators and hashes on index
	updater := func(listing *ListingData) error {
		listing.ModeratorIDs = moderators
		if hash, ok := hashes[listing.Slug]; ok {
			listing.Hash = hash
		}
		return nil
	}
	return n.UpdateEachListingOnIndex(updater)
}

// NotifyModerators - notify moderators(peers)
func (n *OpenBazaarNode) NotifyModerators(moderators []string) error {
	settings, err := n.Datastore.Settings().Get()
	if err != nil {
		return err
	}
	currentMods := make(map[string]bool)
	if settings.StoreModerators != nil {
		for _, mod := range *settings.StoreModerators {
			currentMods[mod] = true
		}
	}
	var addedMods []string
	for _, mod := range moderators {
		if !currentMods[mod] {
			addedMods = append(addedMods, mod)
		} else {
			delete(currentMods, mod)
		}
	}

	removedMods := currentMods

	for _, mod := range addedMods {
		go n.SendModeratorAdd(mod)
	}
	for mod := range removedMods {
		go n.SendModeratorRemove(mod)
	}
	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/url"
	"time"

	"github.com/aerokube/selenoid/config"
	"github.com/aerokube/selenoid/session"
	"github.com/docker/docker/api/types"
	ctr "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	"strings"
)

const (
	comma    = ","
	sysAdmin = "SYS_ADMIN"
)

// Docker - docker container manager
type Docker struct {
	ServiceBase
	Environment
	session.Caps
	LogConfig *ctr.LogConfig
	Client    *client.Client
}

type portConfig struct {
	SeleniumPort nat.Port
	VNCPort      nat.Port
	PortBindings nat.PortMap
	ExposedPorts map[nat.Port]struct{}
}

// StartWithCancel - Starter interface implementation
func (d *Docker) StartWithCancel() (*StartedService, error) {
	portConfig, err := getPortConfig(d.Service, d.Caps, d.Environment)
	if err != nil {
		return nil, fmt.Errorf("configuring ports: %v", err)
	}
	selenium := portConfig.SeleniumPort
	vnc := portConfig.VNCPort
	requestId := d.RequestId
	image := d.Service.Image
	ctx := context.Background()
	log.Printf("[%d] [CREATING_CONTAINER] [%s]\n", requestId, image)
	hostConfig := ctr.HostConfig{
		Binds:        d.Service.Volumes,
		AutoRemove:   true,
		PortBindings: portConfig.PortBindings,
		LogConfig:    getLogConfig(*d.LogConfig, d.Caps),
		NetworkMode:  ctr.NetworkMode(d.Network),
		Tmpfs:        d.Service.Tmpfs,
		ShmSize:      getShmSize(d.Service),
		Privileged:   d.Privileged,
		Resources: ctr.Resources{
			Memory:   d.Memory,
			NanoCPUs: d.CPU,
		},
		ExtraHosts: getExtraHosts(d.Service, d.Caps),
	}
	if !d.Privileged {
		hostConfig.CapAdd = strslice.StrSlice{sysAdmin}
	}
	if d.ApplicationContainers != "" {
		links := strings.Split(d.ApplicationContainers, comma)
		hostConfig.Links = links
	}
	cl := d.Client
	env := getEnv(d.ServiceBase, d.Caps)
	container, err := cl.ContainerCreate(ctx,
		&ctr.Config{
			Hostname:     getContainerHostname(d.Caps),
			Image:        image.(string),
			Env:          env,
			ExposedPorts: portConfig.ExposedPorts,
		},
		&hostConfig,
		&network.NetworkingConfig{}, "")
	if err != nil {
		return nil, fmt.Errorf("create container: %v", err)
	}
	browserContainerStartTime := time.Now()
	browserContainerId := container.ID
	videoContainerId := ""
	log.Printf("[%d] [STARTING_CONTAINER] [%s] [%s]\n", requestId, image, browserContainerId)
	err = cl.ContainerStart(ctx, browserContainerId, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("start container: %v", err)
	}
	log.Printf("[%d] [CONTAINER_STARTED] [%s] [%s] [%v]\n", requestId, image, browserContainerId, time.Since(browserContainerStartTime))
	stat, err := cl.ContainerInspect(ctx, browserContainerId)
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("inspect container %s: %s", browserContainerId, err)
	}
	_, ok := stat.NetworkSettings.Ports[selenium]
	if !ok {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("no bindings available for %v", selenium)
	}
	seleniumHostPort, vncHostPort := getHostPort(d.Environment, d.Service, d.Caps, stat, selenium, vnc)
	u := &url.URL{Scheme: "http", Host: seleniumHostPort, Path: d.Service.Path}

	if d.Video {
		videoContainerId, err = startVideoContainer(ctx, cl, requestId, browserContainerId, d.Environment, d.Caps)
		if err != nil {
			return nil, fmt.Errorf("start video container: %v", err)
		}
	}

	serviceStartTime := time.Now()
	err = wait(u.String(), d.StartupTimeout)
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("wait: %v", err)
	}
	log.Printf("[%d] [SERVICE_STARTED] [%s] [%s] [%v]\n", requestId, image, browserContainerId, time.Since(serviceStartTime))
	log.Printf("[%d] [PROXY_TO] [%s] [%s] [%s]\n", requestId, image, browserContainerId, u.String())
	s := StartedService{
		Url:         u,
		ID:          browserContainerId,
		VNCHostPort: vncHostPort,
		Cancel: func() {
			if videoContainerId != "" {
				stopVideoContainer(ctx, cl, requestId, videoContainerId)
			}
			removeContainer(ctx, cl, requestId, browserContainerId)
		},
	}
	return &s, nil
}

func getPortConfig(service *config.Browser, caps session.Caps, env Environment) (*portConfig, error) {
	selenium, err := nat.NewPort("tcp", service.Port)
	if err != nil {
		return nil, fmt.Errorf("new selenium port: %v", err)
	}
	exposedPorts := map[nat.Port]struct{}{selenium: {}}
	var vnc nat.Port
	if caps.VNC {
		vnc, err = nat.NewPort("tcp", "5900")
		if err != nil {
			return nil, fmt.Errorf("new vnc port: %v", err)
		}
		exposedPorts[vnc] = struct{}{}
	}
	portBindings := nat.PortMap{}
	if env.IP != "" || !env.InDocker {
		portBindings[selenium] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
		if caps.VNC {
			portBindings[vnc] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
		}
	}
	return &portConfig{
		SeleniumPort: selenium,
		VNCPort:      vnc,
		PortBindings: portBindings,
		ExposedPorts: exposedPorts}, nil
}

func getLogConfig(logConfig ctr.LogConfig, caps session.Caps) ctr.LogConfig {
	if logConfig.Config != nil {
		const tag = "tag"
		_, ok := logConfig.Config[tag]
		if caps.TestName != "" && !ok {
			logConfig.Config[tag] = caps.TestName
		}
	}
	return logConfig
}

func getTimeZone(service ServiceBase, caps session.Caps) *time.Location {
	timeZone := time.Local
	if caps.TimeZone != "" {
		tz, err := time.LoadLocation(caps.TimeZone)
		if err != nil {
			log.Printf("[%d] [BAD_TIMEZONE] [%s]\n", service.RequestId, caps.TimeZone)
		} else {
			timeZone = tz
		}
	}
	return timeZone
}

func getEnv(service ServiceBase, caps session.Caps) []string {
	env := []string{
		fmt.Sprintf("TZ=%s", getTimeZone(service, caps)),
		fmt.Sprintf("SCREEN_RESOLUTION=%s", caps.ScreenResolution),
		fmt.Sprintf("ENABLE_VNC=%v", caps.VNC),
	}
	env = append(env, service.Service.Env...)
	return env
}

func getShmSize(service *config.Browser) int64 {
	if service.ShmSize > 0 {
		return service.ShmSize
	}
	return int64(268435456)
}

func getContainerHostname(caps session.Caps) string {
	if caps.ContainerHostname != "" {
		return caps.ContainerHostname
	}
	return "localhost"
}

func getExtraHosts(service *config.Browser, caps session.Caps) []string {
	extraHosts := service.Hosts
	if caps.HostsEntries != "" {
		extraHosts = append(strings.Split(caps.HostsEntries, comma), extraHosts...)
	}
	return extraHosts
}

func getHostPort(env Environment, service *config.Browser, caps session.Caps, stat types.ContainerJSON, selenium nat.Port, vnc nat.Port) (string, string) {
	seleniumHostPort, vncHostPort := "", ""
	if env.IP == "" {
		if env.InDocker {
			containerIP := getContainerIP(env.Network, stat)
			seleniumHostPort = net.JoinHostPort(containerIP, service.Port)
			if caps.VNC {
				vncHostPort = net.JoinHostPort(containerIP, "5900")
			}
		} else {
			seleniumHostPort = net.JoinHostPort("127.0.0.1", stat.NetworkSettings.Ports[selenium][0].HostPort)
			if caps.VNC {
				vncHostPort = net.JoinHostPort("127.0.0.1", stat.NetworkSettings.Ports[vnc][0].HostPort)
			}
		}
	} else {
		seleniumHostPort = net.JoinHostPort(env.IP, stat.NetworkSettings.Ports[selenium][0].HostPort)
		if caps.VNC {
			vncHostPort = net.JoinHostPort(env.IP, stat.NetworkSettings.Ports[vnc][0].HostPort)
		}
	}
	return seleniumHostPort, vncHostPort
}

func getContainerIP(networkName string, stat types.ContainerJSON) string {
	ns := stat.NetworkSettings
	if ns.IPAddress != "" {
		return stat.NetworkSettings.IPAddress
	}
	if len(ns.Networks) > 0 {
		possibleAddresses := []string{}
		for name, nt := range ns.Networks {
			if nt.IPAddress != "" {
				if name == networkName {
					return nt.IPAddress
				}
				possibleAddresses = append(possibleAddresses, nt.IPAddress)
			}
		}
		if len(possibleAddresses) > 0 {
			return possibleAddresses[0]
		}
	}
	return ""
}

func startVideoContainer(ctx context.Context, cl *client.Client, requestId uint64, browserContainerId string, environ Environment, caps session.Caps) (string, error) {
	videoContainerStartTime := time.Now()
	videoContainerImage := environ.VideoContainerImage
	env := []string{fmt.Sprintf("FILE_NAME=%s", caps.VideoName)}
	videoSize := caps.VideoSize
	if videoSize != "" {
		env = append(env, fmt.Sprintf("VIDEO_SIZE=%s", videoSize))
	}
	log.Printf("[%d] [CREATING_VIDEO_CONTAINER] [%s]\n", requestId, videoContainerImage)
	videoContainer, err := cl.ContainerCreate(ctx,
		&ctr.Config{
			Image: videoContainerImage,
			Env:   env,
		},
		&ctr.HostConfig{
			Binds:       []string{fmt.Sprintf("%s:/data", environ.VideoOutputDir)},
			Links:       []string{fmt.Sprintf("%s:browser", browserContainerId)},
			AutoRemove:  true,
			NetworkMode: ctr.NetworkMode(environ.Network),
		},
		&network.NetworkingConfig{}, "")
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return "", fmt.Errorf("create video container: %v", err)
	}

	videoContainerId := videoContainer.ID
	log.Printf("[%d] [STARTING_VIDEO_CONTAINER] [%s] [%s]\n", requestId, videoContainerImage, videoContainerId)
	err = cl.ContainerStart(ctx, videoContainerId, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		removeContainer(ctx, cl, requestId, videoContainerId)
		return "", fmt.Errorf("start video container: %v", err)
	}
	log.Printf("[%d] [VIDEO_CONTAINER_STARTED] [%s] [%s] [%v]\n", requestId, videoContainerImage, videoContainerId, time.Since(videoContainerStartTime))
	return videoContainerId, nil
}

func stopVideoContainer(ctx context.Context, cli *client.Client, requestId uint64, containerId string) {
	log.Printf("[%d] [STOPPING_VIDEO_CONTAINER] [%s]\n", requestId, containerId)
	err := cli.ContainerKill(ctx, containerId, "TERM")
	if err != nil {
		log.Printf("[%d] [FAILED_TO_STOP_VIDEO_CONTAINER] [%s] [%v]\n", requestId, containerId, err)
		return
	}
	log.Printf("[%d] [STOPPED_VIDEO_CONTAINER] [%s]\n", requestId, containerId)
}

func removeContainer(ctx context.Context, cli *client.Client, requestId uint64, id string) {
	log.Printf("[%d] [REMOVING_CONTAINER] [%s]\n", requestId, id)
	err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true})
	if err != nil {
		log.Printf("[%d] [FAILED_TO_REMOVE_CONTAINER] [%s] [%v]\n", requestId, id, err)
		return
	}
	log.Printf("[%d] [CONTAINER_REMOVED] [%s]\n", requestId, id)
}
<commit_msg>Fixed volume permissions for Mac (related to #255)<commit_after>package service

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/url"
	"time"

	"github.com/aerokube/selenoid/config"
	"github.com/aerokube/selenoid/session"
	"github.com/docker/docker/api/types"
	ctr "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/api/types/strslice"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	"strings"
)

const (
	comma    = ","
	sysAdmin = "SYS_ADMIN"
)

// Docker - docker container manager
type Docker struct {
	ServiceBase
	Environment
	session.Caps
	LogConfig *ctr.LogConfig
	Client    *client.Client
}

type portConfig struct {
	SeleniumPort nat.Port
	VNCPort      nat.Port
	PortBindings nat.PortMap
	ExposedPorts map[nat.Port]struct{}
}

// StartWithCancel - Starter interface implementation
func (d *Docker) StartWithCancel() (*StartedService, error) {
	portConfig, err := getPortConfig(d.Service, d.Caps, d.Environment)
	if err != nil {
		return nil, fmt.Errorf("configuring ports: %v", err)
	}
	selenium := portConfig.SeleniumPort
	vnc := portConfig.VNCPort
	requestId := d.RequestId
	image := d.Service.Image
	ctx := context.Background()
	log.Printf("[%d] [CREATING_CONTAINER] [%s]\n", requestId, image)
	hostConfig := ctr.HostConfig{
		Binds:        d.Service.Volumes,
		AutoRemove:   true,
		PortBindings: portConfig.PortBindings,
		LogConfig:    getLogConfig(*d.LogConfig, d.Caps),
		NetworkMode:  ctr.NetworkMode(d.Network),
		Tmpfs:        d.Service.Tmpfs,
		ShmSize:      getShmSize(d.Service),
		Privileged:   d.Privileged,
		Resources: ctr.Resources{
			Memory:   d.Memory,
			NanoCPUs: d.CPU,
		},
		ExtraHosts: getExtraHosts(d.Service, d.Caps),
	}
	if !d.Privileged {
		hostConfig.CapAdd = strslice.StrSlice{sysAdmin}
	}
	if d.ApplicationContainers != "" {
		links := strings.Split(d.ApplicationContainers, comma)
		hostConfig.Links = links
	}
	cl := d.Client
	env := getEnv(d.ServiceBase, d.Caps)
	container, err := cl.ContainerCreate(ctx,
		&ctr.Config{
			Hostname:     getContainerHostname(d.Caps),
			Image:        image.(string),
			Env:          env,
			ExposedPorts: portConfig.ExposedPorts,
		},
		&hostConfig,
		&network.NetworkingConfig{}, "")
	if err != nil {
		return nil, fmt.Errorf("create container: %v", err)
	}
	browserContainerStartTime := time.Now()
	browserContainerId := container.ID
	videoContainerId := ""
	log.Printf("[%d] [STARTING_CONTAINER] [%s] [%s]\n", requestId, image, browserContainerId)
	err = cl.ContainerStart(ctx, browserContainerId, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("start container: %v", err)
	}
	log.Printf("[%d] [CONTAINER_STARTED] [%s] [%s] [%v]\n", requestId, image, browserContainerId, time.Since(browserContainerStartTime))
	stat, err := cl.ContainerInspect(ctx, browserContainerId)
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("inspect container %s: %s", browserContainerId, err)
	}
	_, ok := stat.NetworkSettings.Ports[selenium]
	if !ok {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("no bindings available for %v", selenium)
	}
	seleniumHostPort, vncHostPort := getHostPort(d.Environment, d.Service, d.Caps, stat, selenium, vnc)
	u := &url.URL{Scheme: "http", Host: seleniumHostPort, Path: d.Service.Path}

	if d.Video {
		videoContainerId, err = startVideoContainer(ctx, cl, requestId, browserContainerId, d.Environment, d.Caps)
		if err != nil {
			return nil, fmt.Errorf("start video container: %v", err)
		}
	}

	serviceStartTime := time.Now()
	err = wait(u.String(), d.StartupTimeout)
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return nil, fmt.Errorf("wait: %v", err)
	}
	log.Printf("[%d] [SERVICE_STARTED] [%s] [%s] [%v]\n", requestId, image, browserContainerId, time.Since(serviceStartTime))
	log.Printf("[%d] [PROXY_TO] [%s] [%s] [%s]\n", requestId, image, browserContainerId, u.String())
	s := StartedService{
		Url:         u,
		ID:          browserContainerId,
		VNCHostPort: vncHostPort,
		Cancel: func() {
			if videoContainerId != "" {
				stopVideoContainer(ctx, cl, requestId, videoContainerId)
			}
			removeContainer(ctx, cl, requestId, browserContainerId)
		},
	}
	return &s, nil
}

func getPortConfig(service *config.Browser, caps session.Caps, env Environment) (*portConfig, error) {
	selenium, err := nat.NewPort("tcp", service.Port)
	if err != nil {
		return nil, fmt.Errorf("new selenium port: %v", err)
	}
	exposedPorts := map[nat.Port]struct{}{selenium: {}}
	var vnc nat.Port
	if caps.VNC {
		vnc, err = nat.NewPort("tcp", "5900")
		if err != nil {
			return nil, fmt.Errorf("new vnc port: %v", err)
		}
		exposedPorts[vnc] = struct{}{}
	}
	portBindings := nat.PortMap{}
	if env.IP != "" || !env.InDocker {
		portBindings[selenium] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
		if caps.VNC {
			portBindings[vnc] = []nat.PortBinding{{HostIP: "0.0.0.0"}}
		}
	}
	return &portConfig{
		SeleniumPort: selenium,
		VNCPort:      vnc,
		PortBindings: portBindings,
		ExposedPorts: exposedPorts}, nil
}

func getLogConfig(logConfig ctr.LogConfig, caps session.Caps) ctr.LogConfig {
	if logConfig.Config != nil {
		const tag = "tag"
		_, ok := logConfig.Config[tag]
		if caps.TestName != "" && !ok {
			logConfig.Config[tag] = caps.TestName
		}
	}
	return logConfig
}

func getTimeZone(service ServiceBase, caps session.Caps) *time.Location {
	timeZone := time.Local
	if caps.TimeZone != "" {
		tz, err := time.LoadLocation(caps.TimeZone)
		if err != nil {
			log.Printf("[%d] [BAD_TIMEZONE] [%s]\n", service.RequestId, caps.TimeZone)
		} else {
			timeZone = tz
		}
	}
	return timeZone
}

func getEnv(service ServiceBase, caps session.Caps) []string {
	env := []string{
		fmt.Sprintf("TZ=%s", getTimeZone(service, caps)),
		fmt.Sprintf("SCREEN_RESOLUTION=%s", caps.ScreenResolution),
		fmt.Sprintf("ENABLE_VNC=%v", caps.VNC),
	}
	env = append(env, service.Service.Env...)
	return env
}

func getShmSize(service *config.Browser) int64 {
	if service.ShmSize > 0 {
		return service.ShmSize
	}
	return int64(268435456)
}

func getContainerHostname(caps session.Caps) string {
	if caps.ContainerHostname != "" {
		return caps.ContainerHostname
	}
	return "localhost"
}

func getExtraHosts(service *config.Browser, caps session.Caps) []string {
	extraHosts := service.Hosts
	if caps.HostsEntries != "" {
		extraHosts = append(strings.Split(caps.HostsEntries, comma), extraHosts...)
	}
	return extraHosts
}

func getHostPort(env Environment, service *config.Browser, caps session.Caps, stat types.ContainerJSON, selenium nat.Port, vnc nat.Port) (string, string) {
	seleniumHostPort, vncHostPort := "", ""
	if env.IP == "" {
		if env.InDocker {
			containerIP := getContainerIP(env.Network, stat)
			seleniumHostPort = net.JoinHostPort(containerIP, service.Port)
			if caps.VNC {
				vncHostPort = net.JoinHostPort(containerIP, "5900")
			}
		} else {
			seleniumHostPort = net.JoinHostPort("127.0.0.1", stat.NetworkSettings.Ports[selenium][0].HostPort)
			if caps.VNC {
				vncHostPort = net.JoinHostPort("127.0.0.1", stat.NetworkSettings.Ports[vnc][0].HostPort)
			}
		}
	} else {
		seleniumHostPort = net.JoinHostPort(env.IP, stat.NetworkSettings.Ports[selenium][0].HostPort)
		if caps.VNC {
			vncHostPort = net.JoinHostPort(env.IP, stat.NetworkSettings.Ports[vnc][0].HostPort)
		}
	}
	return seleniumHostPort, vncHostPort
}

func getContainerIP(networkName string, stat types.ContainerJSON) string {
	ns := stat.NetworkSettings
	if ns.IPAddress != "" {
		return stat.NetworkSettings.IPAddress
	}
	if len(ns.Networks) > 0 {
		possibleAddresses := []string{}
		for name, nt := range ns.Networks {
			if nt.IPAddress != "" {
				if name == networkName {
					return nt.IPAddress
				}
				possibleAddresses = append(possibleAddresses, nt.IPAddress)
			}
		}
		if len(possibleAddresses) > 0 {
			return possibleAddresses[0]
		}
	}
	return ""
}

func startVideoContainer(ctx context.Context, cl *client.Client, requestId uint64, browserContainerId string, environ Environment, caps session.Caps) (string, error) {
	videoContainerStartTime := time.Now()
	videoContainerImage := environ.VideoContainerImage
	env := []string{fmt.Sprintf("FILE_NAME=%s", caps.VideoName)}
	videoSize := caps.VideoSize
	if videoSize != "" {
		env = append(env, fmt.Sprintf("VIDEO_SIZE=%s", videoSize))
	}
	log.Printf("[%d] [CREATING_VIDEO_CONTAINER] [%s]\n", requestId, videoContainerImage)
	videoContainer, err := cl.ContainerCreate(ctx,
		&ctr.Config{
			Image: videoContainerImage,
			Env:   env,
		},
		&ctr.HostConfig{
			Binds:       []string{fmt.Sprintf("%s:/data:rw", environ.VideoOutputDir)},
			Links:       []string{fmt.Sprintf("%s:browser", browserContainerId)},
			AutoRemove:  true,
			NetworkMode: ctr.NetworkMode(environ.Network),
		},
		&network.NetworkingConfig{}, "")
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		return "", fmt.Errorf("create video container: %v", err)
	}

	videoContainerId := videoContainer.ID
	log.Printf("[%d] [STARTING_VIDEO_CONTAINER] [%s] [%s]\n", requestId, videoContainerImage, videoContainerId)
	err = cl.ContainerStart(ctx, videoContainerId, types.ContainerStartOptions{})
	if err != nil {
		removeContainer(ctx, cl, requestId, browserContainerId)
		removeContainer(ctx, cl, requestId, videoContainerId)
		return "", fmt.Errorf("start video container: %v", err)
	}
	log.Printf("[%d] [VIDEO_CONTAINER_STARTED] [%s] [%s] [%v]\n", requestId, videoContainerImage, videoContainerId, time.Since(videoContainerStartTime))
	return videoContainerId, nil
}

func stopVideoContainer(ctx context.Context, cli *client.Client, requestId uint64, containerId string) {
	log.Printf("[%d] [STOPPING_VIDEO_CONTAINER] [%s]\n", requestId, containerId)
	err := cli.ContainerKill(ctx, containerId, "TERM")
	if err != nil {
		log.Printf("[%d] [FAILED_TO_STOP_VIDEO_CONTAINER] [%s] [%v]\n", requestId, containerId, err)
		return
	}
	log.Printf("[%d] [STOPPED_VIDEO_CONTAINER] [%s]\n", requestId, containerId)
}

func removeContainer(ctx context.Context, cli *client.Client, requestId uint64, id string) {
	log.Printf("[%d] [REMOVING_CONTAINER] [%s]\n", requestId, id)
	err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true})
	if err != nil {
		log.Printf("[%d] [FAILED_TO_REMOVE_CONTAINER] [%s] [%v]\n", requestId, id, err)
		return
	}
	log.Printf("[%d] [CONTAINER_REMOVED] [%s]\n", requestId, id)
}
<|endoftext|>


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

// This package provides LDAP client functions.
package ldap

import (
   "github.com/mmitton/asn1-ber"
   "fmt"
   "io/ioutil"
   "os"
)

// LDAP Application Codes
const (
   ApplicationBindRequest           = 0
   ApplicationBindResponse          = 1
   ApplicationUnbindRequest         = 2
   ApplicationSearchRequest         = 3
   ApplicationSearchResultEntry     = 4
   ApplicationSearchResultDone      = 5
   ApplicationModifyRequest         = 6
   ApplicationModifyResponse        = 7
   ApplicationAddRequest            = 8
   ApplicationAddResponse           = 9
   ApplicationDelRequest            = 10
   ApplicationDelResponse           = 11
   ApplicationModifyDNRequest       = 12
   ApplicationModifyDNResponse      = 13
   ApplicationCompareRequest        = 14
   ApplicationCompareResponse       = 15
   ApplicationAbandonRequest        = 16
   ApplicationSearchResultReference = 19
   ApplicationExtendedRequest       = 23
   ApplicationExtendedResponse      = 24
)

var ApplicationMap = map[ uint8 ] string {
   ApplicationBindRequest           : "Bind Request",
   ApplicationBindResponse          : "Bind Response",
   ApplicationUnbindRequest         : "Unbind Request",
   ApplicationSearchRequest         : "Search Request",
   ApplicationSearchResultEntry     : "Search Result Entry",
   ApplicationSearchResultDone      : "Search Result Done",
   ApplicationModifyRequest         : "Modify Request",
   ApplicationModifyResponse        : "Modify Response",
   ApplicationAddRequest            : "Add Request",
   ApplicationAddResponse           : "Add Response",
   ApplicationDelRequest            : "Del Request",
   ApplicationDelResponse           : "Del Response",
   ApplicationModifyDNRequest       : "Modify DN Request",
   ApplicationModifyDNResponse      : "Modify DN Response",
   ApplicationCompareRequest        : "Compare Request",
   ApplicationCompareResponse       : "Compare Response",
   ApplicationAbandonRequest        : "Abandon Request",
   ApplicationSearchResultReference : "Search Result Reference",
   ApplicationExtendedRequest       : "Extended Request",
   ApplicationExtendedResponse      : "Extended Response",
}

// LDAP Result Codes
const (
   LDAPResultSuccess                      =  0
   LDAPResultOperationsError              =  1
   LDAPResultProtocolError                =  2
   LDAPResultTimeLimitExceeded            =  3
   LDAPResultSizeLimitExceeded            =  4
   LDAPResultCompareFalse                 =  5
   LDAPResultCompareTrue                  =  6
   LDAPResultAuthMethodNotSupported       =  7
   LDAPResultStrongAuthRequired           =  8
   LDAPResultReferral                     = 10
   LDAPResultAdminLimitExceeded           = 11
   LDAPResultUnavailableCriticalExtension = 12
   LDAPResultConfidentialityRequired      = 13
   LDAPResultSaslBindInProgress           = 14
   LDAPResultNoSuchAttribute              = 16
   LDAPResultUndefinedAttributeType       = 17
   LDAPResultInappropriateMatching        = 18
   LDAPResultConstraintViolation          = 19
   LDAPResultAttributeOrValueExists       = 20
   LDAPResultInvalidAttributeSyntax       = 21
   LDAPResultNoSuchObject                 = 32
   LDAPResultAliasProblem                 = 33
   LDAPResultInvalidDNSyntax              = 34
   LDAPResultAliasDereferencingProblem    = 36
   LDAPResultInappropriateAuthentication  = 48
   LDAPResultInvalidCredentials           = 49
   LDAPResultInsufficientAccessRights     = 50
   LDAPResultBusy                         = 51
   LDAPResultUnavailable                  = 52
   LDAPResultUnwillingToPerform           = 53
   LDAPResultLoopDetect                   = 54
   LDAPResultNamingViolation              = 64
   LDAPResultObjectClassViolation         = 65
   LDAPResultNotAllowedOnNonLeaf          = 66
   LDAPResultNotAllowedOnRDN              = 67
   LDAPResultEntryAlreadyExists           = 68
   LDAPResultObjectClassModsProhibited    = 69
   LDAPResultAffectsMultipleDSAs          = 71
   LDAPResultOther                        = 80

   ErrorNetwork                           = 200
   ErrorFilterCompile                     = 201
   ErrorFilterDecompile                   = 202
   ErrorDebugging                         = 203
)

var LDAPResultCodeMap = map[uint8] string {
   LDAPResultSuccess                      : "Success",
   LDAPResultOperationsError              : "Operations Error",
   LDAPResultProtocolError                : "Protocol Error",
   LDAPResultTimeLimitExceeded            : "Time Limit Exceeded",
   LDAPResultSizeLimitExceeded            : "Size Limit Exceeded",
   LDAPResultCompareFalse                 : "Compare False",
   LDAPResultCompareTrue                  : "Compare True",
   LDAPResultAuthMethodNotSupported       : "Auth Method Not Supported",
   LDAPResultStrongAuthRequired           : "Strong Auth Required",
   LDAPResultReferral                     : "Referral",
   LDAPResultAdminLimitExceeded           : "Admin Limit Exceeded",
   LDAPResultUnavailableCriticalExtension : "Unavailable Critical Extension",
   LDAPResultConfidentialityRequired      : "Confidentiality Required",
   LDAPResultSaslBindInProgress           : "Sasl Bind In Progress",
   LDAPResultNoSuchAttribute              : "No Such Attribute",
   LDAPResultUndefinedAttributeType       : "Undefined Attribute Type",
   LDAPResultInappropriateMatching        : "Inappropriate Matching",
   LDAPResultConstraintViolation          : "Constraint Violation",
   LDAPResultAttributeOrValueExists       : "Attribute Or Value Exists",
   LDAPResultInvalidAttributeSyntax       : "Invalid Attribute Syntax",
   LDAPResultNoSuchObject                 : "No Such Object",
   LDAPResultAliasProblem                 : "Alias Problem",
   LDAPResultInvalidDNSyntax              : "Invalid DN Syntax",
   LDAPResultAliasDereferencingProblem    : "Alias Dereferencing Problem",
   LDAPResultInappropriateAuthentication  : "Inappropriate Authentication",
   LDAPResultInvalidCredentials           : "Invalid Credentials",
   LDAPResultInsufficientAccessRights     : "Insufficient Access Rights",
   LDAPResultBusy                         : "Busy",
   LDAPResultUnavailable                  : "Unavailable",
   LDAPResultUnwillingToPerform           : "Unwilling To Perform",
   LDAPResultLoopDetect                   : "Loop Detect",
   LDAPResultNamingViolation              : "Naming Violation",
   LDAPResultObjectClassViolation         : "Object Class Violation",
   LDAPResultNotAllowedOnNonLeaf          : "Not Allowed On Non Leaf",
   LDAPResultNotAllowedOnRDN              : "Not Allowed On RDN",
   LDAPResultEntryAlreadyExists           : "Entry Already Exists",
   LDAPResultObjectClassModsProhibited    : "Object Class Mods Prohibited",
   LDAPResultAffectsMultipleDSAs          : "Affects Multiple DSAs",
   LDAPResultOther                        : "Other",
}

// Adds descriptions to an LDAP Response packet for debugging
func addLDAPDescriptions( packet *ber.Packet ) (err *Error) {
   defer func() {
      if r := recover(); r != nil {
         err = NewError( ErrorDebugging, os.NewError( "Cannot process packet to add descriptions" ) )
      }
   }()
   packet.Description = "LDAP Response"
   packet.Children[ 0 ].Description = "Message ID";

   application := packet.Children[ 1 ].Tag
   packet.Children[ 1 ].Description = ApplicationMap[ application ]

   switch application {
      case ApplicationBindRequest:
         addRequestDescriptions( packet )
      case ApplicationBindResponse:
         addDefaultLDAPResponseDescriptions( packet )
      case ApplicationUnbindRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchResultEntry:
         packet.Children[ 1 ].Children[ 0 ].Description = "Object Name"
         packet.Children[ 1 ].Children[ 1 ].Description = "Attributes"
         for _, child := range packet.Children[ 1 ].Children[ 1 ].Children {
            child.Description = "Attribute"
            child.Children[ 0 ].Description = "Attribute Name"
            child.Children[ 1 ].Description = "Attribute Values"
            for _, grandchild := range child.Children[ 1 ].Children {
               grandchild.Description = "Attribute Value"
            }
         }
         if len( packet.Children ) == 3 {
            addControlDescriptions( packet.Children[ 2 ] )
         }
      case ApplicationSearchResultDone:
         addDefaultLDAPResponseDescriptions( packet )
      case ApplicationModifyRequest:
         addRequestDescriptions( packet )
      case ApplicationModifyResponse:
      case ApplicationAddRequest:
         addRequestDescriptions( packet )
      case ApplicationAddResponse:
      case ApplicationDelRequest:
         addRequestDescriptions( packet )
      case ApplicationDelResponse:
      case ApplicationModifyDNRequest:
         addRequestDescriptions( packet )
      case ApplicationModifyDNResponse:
      case ApplicationCompareRequest:
         addRequestDescriptions( packet )
      case ApplicationCompareResponse:
      case ApplicationAbandonRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchResultReference:
      case ApplicationExtendedRequest:
         addRequestDescriptions( packet )
      case ApplicationExtendedResponse:
   }

   return nil
}

func addControlDescriptions( packet *ber.Packet ) {
   packet.Description = "Controls"
   for _, child := range packet.Children {
      child.Description = "Control"
      child.Children[ 0 ].Description = "Control Type (" + ControlTypeMap[ child.Children[ 0 ].Value.(string) ] + ")"
      value := child.Children[ 1 ]
      if len( child.Children ) == 3 {
         child.Children[ 1 ].Description = "Criticality"
         value = child.Children[ 2 ]
      }
      value.Description = "Control Value"

      switch child.Children[ 0 ].Value.(string) {
         case ControlTypePaging:
            value.Description += " (Paging)"
            if value.Value != nil {
               value_children := ber.DecodePacket( value.Data.Bytes() )
               value.Data.Truncate( 0 )
               value.Value = nil
               value_children.Children[ 1 ].Value = value_children.Children[ 1 ].Data.Bytes()
               value.AppendChild( value_children )
            }
            value.Children[ 0 ].Description = "Real Search Control Value"
            value.Children[ 0 ].Children[ 0 ].Description = "Paging Size"
            value.Children[ 0 ].Children[ 1 ].Description = "Cookie"
      }
   }
}

func addRequestDescriptions( packet *ber.Packet ) {
   packet.Description = "LDAP Request"
   packet.Children[ 0 ].Description = "Message ID"
   packet.Children[ 1 ].Description = ApplicationMap[ packet.Children[ 1 ].Tag ];
   if len( packet.Children ) == 3 {
      addControlDescriptions( packet.Children[ 2 ] )
   }
}

func addDefaultLDAPResponseDescriptions( packet *ber.Packet ) {
   resultCode := packet.Children[ 1 ].Children[ 0 ].Value.(uint64)
   packet.Children[ 1 ].Children[ 0 ].Description = "Result Code (" + LDAPResultCodeMap[ uint8(resultCode) ] + ")";
   packet.Children[ 1 ].Children[ 1 ].Description = "Matched DN";
   packet.Children[ 1 ].Children[ 2 ].Description = "Error Message";
   if len( packet.Children[ 1 ].Children ) > 3 {
      packet.Children[ 1 ].Children[ 3 ].Description = "Referral";
   }
   if len( packet.Children ) == 3 {
      addControlDescriptions( packet.Children[ 2 ] )
   }
}

func DebugBinaryFile( FileName string ) *Error {
   file, err := ioutil.ReadFile( FileName )
   if err != nil {
      return NewError( ErrorDebugging, err )
   }
   ber.PrintBytes( file, "" )
   packet := ber.DecodePacket( file )
   addLDAPDescriptions( packet )
   ber.PrintPacket( packet )

   return nil
}

type Error struct {
   Err os.Error
   ResultCode uint8
}

func (e *Error) String() string {
   return fmt.Sprintf( "LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[ e.ResultCode ], e.Err.String() )
}

func NewError( ResultCode uint8, Err os.Error ) (* Error) {
   return &Error{ ResultCode: ResultCode, Err: Err }
}
<commit_msg>Added getLDAPResultCode function to get result code and description from result packet<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package provides LDAP client functions.
package ldap

import (
   "github.com/mmitton/asn1-ber"
   "fmt"
   "io/ioutil"
   "os"
)

// LDAP Application Codes
const (
   ApplicationBindRequest           = 0
   ApplicationBindResponse          = 1
   ApplicationUnbindRequest         = 2
   ApplicationSearchRequest         = 3
   ApplicationSearchResultEntry     = 4
   ApplicationSearchResultDone      = 5
   ApplicationModifyRequest         = 6
   ApplicationModifyResponse        = 7
   ApplicationAddRequest            = 8
   ApplicationAddResponse           = 9
   ApplicationDelRequest            = 10
   ApplicationDelResponse           = 11
   ApplicationModifyDNRequest       = 12
   ApplicationModifyDNResponse      = 13
   ApplicationCompareRequest        = 14
   ApplicationCompareResponse       = 15
   ApplicationAbandonRequest        = 16
   ApplicationSearchResultReference = 19
   ApplicationExtendedRequest       = 23
   ApplicationExtendedResponse      = 24
)

var ApplicationMap = map[ uint8 ] string {
   ApplicationBindRequest           : "Bind Request",
   ApplicationBindResponse          : "Bind Response",
   ApplicationUnbindRequest         : "Unbind Request",
   ApplicationSearchRequest         : "Search Request",
   ApplicationSearchResultEntry     : "Search Result Entry",
   ApplicationSearchResultDone      : "Search Result Done",
   ApplicationModifyRequest         : "Modify Request",
   ApplicationModifyResponse        : "Modify Response",
   ApplicationAddRequest            : "Add Request",
   ApplicationAddResponse           : "Add Response",
   ApplicationDelRequest            : "Del Request",
   ApplicationDelResponse           : "Del Response",
   ApplicationModifyDNRequest       : "Modify DN Request",
   ApplicationModifyDNResponse      : "Modify DN Response",
   ApplicationCompareRequest        : "Compare Request",
   ApplicationCompareResponse       : "Compare Response",
   ApplicationAbandonRequest        : "Abandon Request",
   ApplicationSearchResultReference : "Search Result Reference",
   ApplicationExtendedRequest       : "Extended Request",
   ApplicationExtendedResponse      : "Extended Response",
}

// LDAP Result Codes
const (
   LDAPResultSuccess                      =  0
   LDAPResultOperationsError              =  1
   LDAPResultProtocolError                =  2
   LDAPResultTimeLimitExceeded            =  3
   LDAPResultSizeLimitExceeded            =  4
   LDAPResultCompareFalse                 =  5
   LDAPResultCompareTrue                  =  6
   LDAPResultAuthMethodNotSupported       =  7
   LDAPResultStrongAuthRequired           =  8
   LDAPResultReferral                     = 10
   LDAPResultAdminLimitExceeded           = 11
   LDAPResultUnavailableCriticalExtension = 12
   LDAPResultConfidentialityRequired      = 13
   LDAPResultSaslBindInProgress           = 14
   LDAPResultNoSuchAttribute              = 16
   LDAPResultUndefinedAttributeType       = 17
   LDAPResultInappropriateMatching        = 18
   LDAPResultConstraintViolation          = 19
   LDAPResultAttributeOrValueExists       = 20
   LDAPResultInvalidAttributeSyntax       = 21
   LDAPResultNoSuchObject                 = 32
   LDAPResultAliasProblem                 = 33
   LDAPResultInvalidDNSyntax              = 34
   LDAPResultAliasDereferencingProblem    = 36
   LDAPResultInappropriateAuthentication  = 48
   LDAPResultInvalidCredentials           = 49
   LDAPResultInsufficientAccessRights     = 50
   LDAPResultBusy                         = 51
   LDAPResultUnavailable                  = 52
   LDAPResultUnwillingToPerform           = 53
   LDAPResultLoopDetect                   = 54
   LDAPResultNamingViolation              = 64
   LDAPResultObjectClassViolation         = 65
   LDAPResultNotAllowedOnNonLeaf          = 66
   LDAPResultNotAllowedOnRDN              = 67
   LDAPResultEntryAlreadyExists           = 68
   LDAPResultObjectClassModsProhibited    = 69
   LDAPResultAffectsMultipleDSAs          = 71
   LDAPResultOther                        = 80

   ErrorNetwork                           = 200
   ErrorFilterCompile                     = 201
   ErrorFilterDecompile                   = 202
   ErrorDebugging                         = 203
)

var LDAPResultCodeMap = map[uint8] string {
   LDAPResultSuccess                      : "Success",
   LDAPResultOperationsError              : "Operations Error",
   LDAPResultProtocolError                : "Protocol Error",
   LDAPResultTimeLimitExceeded            : "Time Limit Exceeded",
   LDAPResultSizeLimitExceeded            : "Size Limit Exceeded",
   LDAPResultCompareFalse                 : "Compare False",
   LDAPResultCompareTrue                  : "Compare True",
   LDAPResultAuthMethodNotSupported       : "Auth Method Not Supported",
   LDAPResultStrongAuthRequired           : "Strong Auth Required",
   LDAPResultReferral                     : "Referral",
   LDAPResultAdminLimitExceeded           : "Admin Limit Exceeded",
   LDAPResultUnavailableCriticalExtension : "Unavailable Critical Extension",
   LDAPResultConfidentialityRequired      : "Confidentiality Required",
   LDAPResultSaslBindInProgress           : "Sasl Bind In Progress",
   LDAPResultNoSuchAttribute              : "No Such Attribute",
   LDAPResultUndefinedAttributeType       : "Undefined Attribute Type",
   LDAPResultInappropriateMatching        : "Inappropriate Matching",
   LDAPResultConstraintViolation          : "Constraint Violation",
   LDAPResultAttributeOrValueExists       : "Attribute Or Value Exists",
   LDAPResultInvalidAttributeSyntax       : "Invalid Attribute Syntax",
   LDAPResultNoSuchObject                 : "No Such Object",
   LDAPResultAliasProblem                 : "Alias Problem",
   LDAPResultInvalidDNSyntax              : "Invalid DN Syntax",
   LDAPResultAliasDereferencingProblem    : "Alias Dereferencing Problem",
   LDAPResultInappropriateAuthentication  : "Inappropriate Authentication",
   LDAPResultInvalidCredentials           : "Invalid Credentials",
   LDAPResultInsufficientAccessRights     : "Insufficient Access Rights",
   LDAPResultBusy                         : "Busy",
   LDAPResultUnavailable                  : "Unavailable",
   LDAPResultUnwillingToPerform           : "Unwilling To Perform",
   LDAPResultLoopDetect                   : "Loop Detect",
   LDAPResultNamingViolation              : "Naming Violation",
   LDAPResultObjectClassViolation         : "Object Class Violation",
   LDAPResultNotAllowedOnNonLeaf          : "Not Allowed On Non Leaf",
   LDAPResultNotAllowedOnRDN              : "Not Allowed On RDN",
   LDAPResultEntryAlreadyExists           : "Entry Already Exists",
   LDAPResultObjectClassModsProhibited    : "Object Class Mods Prohibited",
   LDAPResultAffectsMultipleDSAs          : "Affects Multiple DSAs",
   LDAPResultOther                        : "Other",
}

// Adds descriptions to an LDAP Response packet for debugging
func addLDAPDescriptions( packet *ber.Packet ) (err *Error) {
   defer func() {
      if r := recover(); r != nil {
         err = NewError( ErrorDebugging, os.NewError( "Cannot process packet to add descriptions" ) )
      }
   }()
   packet.Description = "LDAP Response"
   packet.Children[ 0 ].Description = "Message ID";

   application := packet.Children[ 1 ].Tag
   packet.Children[ 1 ].Description = ApplicationMap[ application ]

   switch application {
      case ApplicationBindRequest:
         addRequestDescriptions( packet )
      case ApplicationBindResponse:
         addDefaultLDAPResponseDescriptions( packet )
      case ApplicationUnbindRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchResultEntry:
         packet.Children[ 1 ].Children[ 0 ].Description = "Object Name"
         packet.Children[ 1 ].Children[ 1 ].Description = "Attributes"
         for _, child := range packet.Children[ 1 ].Children[ 1 ].Children {
            child.Description = "Attribute"
            child.Children[ 0 ].Description = "Attribute Name"
            child.Children[ 1 ].Description = "Attribute Values"
            for _, grandchild := range child.Children[ 1 ].Children {
               grandchild.Description = "Attribute Value"
            }
         }
         if len( packet.Children ) == 3 {
            addControlDescriptions( packet.Children[ 2 ] )
         }
      case ApplicationSearchResultDone:
         addDefaultLDAPResponseDescriptions( packet )
      case ApplicationModifyRequest:
         addRequestDescriptions( packet )
      case ApplicationModifyResponse:
      case ApplicationAddRequest:
         addRequestDescriptions( packet )
      case ApplicationAddResponse:
      case ApplicationDelRequest:
         addRequestDescriptions( packet )
      case ApplicationDelResponse:
      case ApplicationModifyDNRequest:
         addRequestDescriptions( packet )
      case ApplicationModifyDNResponse:
      case ApplicationCompareRequest:
         addRequestDescriptions( packet )
      case ApplicationCompareResponse:
      case ApplicationAbandonRequest:
         addRequestDescriptions( packet )
      case ApplicationSearchResultReference:
      case ApplicationExtendedRequest:
         addRequestDescriptions( packet )
      case ApplicationExtendedResponse:
   }

   return nil
}

func addControlDescriptions( packet *ber.Packet ) {
   packet.Description = "Controls"
   for _, child := range packet.Children {
      child.Description = "Control"
      child.Children[ 0 ].Description = "Control Type (" + ControlTypeMap[ child.Children[ 0 ].Value.(string) ] + ")"
      value := child.Children[ 1 ]
      if len( child.Children ) == 3 {
         child.Children[ 1 ].Description = "Criticality"
         value = child.Children[ 2 ]
      }
      value.Description = "Control Value"

      switch child.Children[ 0 ].Value.(string) {
         case ControlTypePaging:
            value.Description += " (Paging)"
            if value.Value != nil {
               value_children := ber.DecodePacket( value.Data.Bytes() )
               value.Data.Truncate( 0 )
               value.Value = nil
               value_children.Children[ 1 ].Value = value_children.Children[ 1 ].Data.Bytes()
               value.AppendChild( value_children )
            }
            value.Children[ 0 ].Description = "Real Search Control Value"
            value.Children[ 0 ].Children[ 0 ].Description = "Paging Size"
            value.Children[ 0 ].Children[ 1 ].Description = "Cookie"
      }
   }
}

func addRequestDescriptions( packet *ber.Packet ) {
   packet.Description = "LDAP Request"
   packet.Children[ 0 ].Description = "Message ID"
   packet.Children[ 1 ].Description = ApplicationMap[ packet.Children[ 1 ].Tag ];
   if len( packet.Children ) == 3 {
      addControlDescriptions( packet.Children[ 2 ] )
   }
}

func addDefaultLDAPResponseDescriptions( packet *ber.Packet ) {
   resultCode := packet.Children[ 1 ].Children[ 0 ].Value.(uint64)
   packet.Children[ 1 ].Children[ 0 ].Description = "Result Code (" + LDAPResultCodeMap[ uint8(resultCode) ] + ")";
   packet.Children[ 1 ].Children[ 1 ].Description = "Matched DN";
   packet.Children[ 1 ].Children[ 2 ].Description = "Error Message";
   if len( packet.Children[ 1 ].Children ) > 3 {
      packet.Children[ 1 ].Children[ 3 ].Description = "Referral";
   }
   if len( packet.Children ) == 3 {
      addControlDescriptions( packet.Children[ 2 ] )
   }
}

func DebugBinaryFile( FileName string ) *Error {
   file, err := ioutil.ReadFile( FileName )
   if err != nil {
      return NewError( ErrorDebugging, err )
   }
   ber.PrintBytes( file, "" )
   packet := ber.DecodePacket( file )
   addLDAPDescriptions( packet )
   ber.PrintPacket( packet )

   return nil
}

type Error struct {
   Err os.Error
   ResultCode uint8
}

func (e *Error) String() string {
   return fmt.Sprintf( "LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[ e.ResultCode ], e.Err.String() )
}

func NewError( ResultCode uint8, Err os.Error ) (* Error) {
   return &Error{ ResultCode: ResultCode, Err: Err }
}

func getLDAPResultCode( p *ber.Packet ) ( code uint8, description string ) {
   if len( p.Children ) >= 2 {
      response := p.Children[ 1 ]
      if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len( response.Children ) == 3 {
         code = uint8(response.Children[ 0 ].Value.(uint64))
         description = response.Children[ 2 ].Value.(string)
         return
      }
   }

   code = ErrorNetwork
   description = "Invalid packet format"
   return
}
<|endoftext|>