<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package rocksdb

import (
	"bytes"
	"errors"
	"os"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/linxGnu/grocksdb"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/nodb"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/perms"
)

const (
	// Name is the name of this database for database switches
	Name = "rocks"

	MemoryBudget   = 512 * 1024 * 1024 // 512 MiB
	BitsPerKey     = 10                // 10 bits
	BlockCacheSize = 12 * 1024 * 1024  // 12 MiB
	BlockSize      = 8 * 1024          // 8 KiB

	// rocksDBByteOverhead is the number of bytes of constant overhead that
	// should be added to a batch size per operation.
	rocksDBByteOverhead = 8
)

// Database is a persistent key-value store. Apart from basic data storage
// functionality it also supports batch writes and iterating over the keyspace
// in binary-alphabetical order.
type Database struct {
	lock            sync.RWMutex
	db              *grocksdb.DB
	readOptions     *grocksdb.ReadOptions
	iteratorOptions *grocksdb.ReadOptions
	writeOptions    *grocksdb.WriteOptions

	log logging.Logger

	// 1 if there was previously an error other than "not found" or "closed"
	// while performing a db operation. If [errored] == 1, Has, Get, Put,
	// Delete and batch writes fail with ErrAvoidCorruption.
	errored uint64
}

// New returns a wrapped RocksDB object.
func New(file string, log logging.Logger) (*Database, error) {
	filter := grocksdb.NewBloomFilter(BitsPerKey)

	blockOptions := grocksdb.NewDefaultBlockBasedTableOptions()
	blockOptions.SetBlockCache(grocksdb.NewLRUCache(BlockCacheSize))
	blockOptions.SetBlockSize(BlockSize)
	blockOptions.SetFilterPolicy(filter)

	options := grocksdb.NewDefaultOptions()
	options.SetCreateIfMissing(true)
	options.OptimizeUniversalStyleCompaction(MemoryBudget)
	options.SetBlockBasedTableFactory(blockOptions)

	if err := os.MkdirAll(file, perms.ReadWriteExecute); err != nil {
		return nil, err
	}

	db, err := grocksdb.OpenDb(options, file)
	if err != nil {
		return nil, err
	}

	iteratorOptions := grocksdb.NewDefaultReadOptions()
	iteratorOptions.SetFillCache(false)

	return &Database{
		db:              db,
		readOptions:     grocksdb.NewDefaultReadOptions(),
		iteratorOptions: iteratorOptions,
		writeOptions:    grocksdb.NewDefaultWriteOptions(),
		log:             log,
	}, nil
}

// Has returns if the key is set in the database
func (db *Database) Has(key []byte) (bool, error) {
	_, err := db.Get(key)
	switch err {
	case nil:
		return true, nil
	case database.ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

// Get returns the value the key maps to in the database
func (db *Database) Get(key []byte) ([]byte, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return nil, database.ErrClosed
	case db.corrupted():
		return nil, database.ErrAvoidCorruption
	}

	value, err := db.db.GetBytes(db.readOptions, key)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
		return nil, err
	}
	if value != nil {
		return value, nil
	}
	return nil, database.ErrNotFound
}

// Put sets the value of the provided key to the provided value
func (db *Database) Put(key []byte, value []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	err := db.db.Put(db.writeOptions, key, value)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
	}
	return err
}

// Delete removes the key from the database
func (db *Database) Delete(key []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	err := db.db.Delete(db.writeOptions, key)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
	}
	return err
}

// NewBatch creates a write/delete-only buffer that is atomically committed to
// the database when write is called
func (db *Database) NewBatch() database.Batch {
	b := grocksdb.NewWriteBatch()
	runtime.SetFinalizer(b, func(b *grocksdb.WriteBatch) {
		b.Destroy()
	})
	return &batch{
		batch: b,
		db:    db,
	}
}

// Inner returns itself
func (b *batch) Inner() database.Batch { return b }

var errFailedToCreateIterator = errors.New("failed to create iterator")

// NewIterator creates a lexicographically ordered iterator over the database
func (db *Database) NewIterator() database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(nil)
	return &iter{
		it: it,
		db: db,
	}
}

// NewIteratorWithStart creates a lexicographically ordered iterator over the
// database starting at the provided key
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(start)
	return &iter{
		it: it,
		db: db,
	}
}

// NewIteratorWithPrefix creates a lexicographically ordered iterator over the
// database ignoring keys that do not start with the provided prefix
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(prefix)
	return &iter{
		it:            it,
		db:            db,
		expectsPrefix: true,
		prefix:        prefix,
	}
}

// NewIteratorWithStartAndPrefix creates a lexicographically ordered iterator
// over the database starting at start and ignoring keys that do not start with
// the provided prefix
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	if bytes.Compare(start, prefix) == 1 {
		it.Seek(start)
	} else {
		it.Seek(prefix)
	}
	return &iter{
		it:            it,
		db:            db,
		expectsPrefix: true,
		prefix:        prefix,
	}
}

// Stat returns a particular internal stat of the database.
func (db *Database) Stat(property string) (string, error) {
	return "", database.ErrNotFound
}

// Compact the underlying DB for the given key range.
// Specifically, deleted and overwritten versions are discarded,
// and the data is rearranged to reduce the cost of operations
// needed to access the data. This operation should typically only
// be invoked by users who understand the underlying implementation.
//
// A nil start is treated as a key before all keys in the DB.
// And a nil limit is treated as a key after all keys in the DB.
// Therefore if both are nil then it will compact entire DB.
func (db *Database) Compact(start []byte, limit []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	db.db.CompactRange(grocksdb.Range{Start: start, Limit: limit})
	return nil
}

// Close implements the Database interface
func (db *Database) Close() error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.db == nil {
		return database.ErrClosed
	}

	db.readOptions.Destroy()
	db.iteratorOptions.Destroy()
	db.writeOptions.Destroy()
	db.db.Close()

	db.db = nil
	return nil
}

func (db *Database) corrupted() bool {
	return atomic.LoadUint64(&db.errored) == 1
}

// batch is a wrapper around a levelDB batch to contain sizes.
type batch struct {
	batch *grocksdb.WriteBatch
	db    *Database
	size  int
}

// Put the value into the batch for later writing
func (b *batch) Put(key, value []byte) error {
	b.batch.Put(key, value)
	b.size += len(key) + len(value) + rocksDBByteOverhead
	return nil
}

// Delete the key during writing
func (b *batch) Delete(key []byte) error {
	b.batch.Delete(key)
	b.size += len(key) + rocksDBByteOverhead
	return nil
}

// Size retrieves the amount of data queued up for writing.
func (b *batch) Size() int { return b.size }

// Write flushes any accumulated data to disk.
func (b *batch) Write() error {
	b.db.lock.RLock()
	defer b.db.lock.RUnlock()

	switch {
	case b.db.db == nil:
		return database.ErrClosed
	case b.db.corrupted():
		return database.ErrAvoidCorruption
	}
	return b.db.db.Write(b.db.writeOptions, b.batch)
}

// Reset resets the batch for reuse.
func (b *batch) Reset() {
	b.batch.Clear()
	b.size = 0
}

// Replay the batch contents.
func (b *batch) Replay(w database.KeyValueWriter) error {
	it := b.batch.NewIterator()
	for it.Next() {
		rec := it.Record()
		switch rec.Type {
		case
			grocksdb.WriteBatchDeletionRecord,
			grocksdb.WriteBatchSingleDeletionRecord:
			if err := w.Delete(rec.Key); err != nil {
				return err
			}
		case grocksdb.WriteBatchValueRecord:
			if err := w.Put(rec.Key, rec.Value); err != nil {
				return err
			}
		}
	}
	return nil
}

type iter struct {
	it            *grocksdb.Iterator
	db            *Database
	expectsPrefix bool
	prefix        []byte
	started       bool
	key           []byte
	value         []byte
}

// Error implements the Iterator interface
func (it *iter) Error() error { return it.it.Err() }

// Key implements the Iterator interface
func (it *iter) Key() []byte {
	return utils.CopyBytes(it.key)
}

// Value implements the Iterator interface
func (it *iter) Value() []byte {
	return utils.CopyBytes(it.value)
}

func (it *iter) Release() {
	it.db.lock.RLock()
	defer it.db.lock.RUnlock()

	if it.db.db != nil {
		it.it.Close()
	}
}

func (it *iter) Next() bool {
	if it.started {
		it.it.Next()
	}
	it.started = true
	valid := it.it.Valid()
	if !valid {
		it.key = nil
		it.value = nil
	} else {
		it.key = it.it.Key().Data()
		it.value = it.it.Value().Data()
		if it.expectsPrefix && !bytes.HasPrefix(it.key, it.prefix) {
			valid = false
			it.key = nil
			it.value = nil
		}
	}
	return valid
}
<commit_msg>moved var block to head of file<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package rocksdb

import (
	"bytes"
	"errors"
	"os"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/linxGnu/grocksdb"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/nodb"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/perms"
)

const (
	// Name is the name of this database for database switches
	Name = "rocks"

	MemoryBudget   = 512 * 1024 * 1024 // 512 MiB
	BitsPerKey     = 10                // 10 bits
	BlockCacheSize = 12 * 1024 * 1024  // 12 MiB
	BlockSize      = 8 * 1024          // 8 KiB

	// rocksDBByteOverhead is the number of bytes of constant overhead that
	// should be added to a batch size per operation.
	rocksDBByteOverhead = 8
)

var errFailedToCreateIterator = errors.New("failed to create iterator")

// Database is a persistent key-value store. Apart from basic data storage
// functionality it also supports batch writes and iterating over the keyspace
// in binary-alphabetical order.
type Database struct {
	lock            sync.RWMutex
	db              *grocksdb.DB
	readOptions     *grocksdb.ReadOptions
	iteratorOptions *grocksdb.ReadOptions
	writeOptions    *grocksdb.WriteOptions

	log logging.Logger

	// 1 if there was previously an error other than "not found" or "closed"
	// while performing a db operation. If [errored] == 1, Has, Get, Put,
	// Delete and batch writes fail with ErrAvoidCorruption.
	errored uint64
}

// New returns a wrapped RocksDB object.
func New(file string, log logging.Logger) (*Database, error) {
	filter := grocksdb.NewBloomFilter(BitsPerKey)

	blockOptions := grocksdb.NewDefaultBlockBasedTableOptions()
	blockOptions.SetBlockCache(grocksdb.NewLRUCache(BlockCacheSize))
	blockOptions.SetBlockSize(BlockSize)
	blockOptions.SetFilterPolicy(filter)

	options := grocksdb.NewDefaultOptions()
	options.SetCreateIfMissing(true)
	options.OptimizeUniversalStyleCompaction(MemoryBudget)
	options.SetBlockBasedTableFactory(blockOptions)

	if err := os.MkdirAll(file, perms.ReadWriteExecute); err != nil {
		return nil, err
	}

	db, err := grocksdb.OpenDb(options, file)
	if err != nil {
		return nil, err
	}

	iteratorOptions := grocksdb.NewDefaultReadOptions()
	iteratorOptions.SetFillCache(false)

	return &Database{
		db:              db,
		readOptions:     grocksdb.NewDefaultReadOptions(),
		iteratorOptions: iteratorOptions,
		writeOptions:    grocksdb.NewDefaultWriteOptions(),
		log:             log,
	}, nil
}

// Has returns if the key is set in the database
func (db *Database) Has(key []byte) (bool, error) {
	_, err := db.Get(key)
	switch err {
	case nil:
		return true, nil
	case database.ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

// Get returns the value the key maps to in the database
func (db *Database) Get(key []byte) ([]byte, error) {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return nil, database.ErrClosed
	case db.corrupted():
		return nil, database.ErrAvoidCorruption
	}

	value, err := db.db.GetBytes(db.readOptions, key)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
		return nil, err
	}
	if value != nil {
		return value, nil
	}
	return nil, database.ErrNotFound
}

// Put sets the value of the provided key to the provided value
func (db *Database) Put(key []byte, value []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	err := db.db.Put(db.writeOptions, key, value)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
	}
	return err
}

// Delete removes the key from the database
func (db *Database) Delete(key []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	err := db.db.Delete(db.writeOptions, key)
	if err != nil {
		atomic.StoreUint64(&db.errored, 1)
	}
	return err
}

// NewBatch creates a write/delete-only buffer that is atomically committed to
// the database when write is called
func (db *Database) NewBatch() database.Batch {
	b := grocksdb.NewWriteBatch()
	runtime.SetFinalizer(b, func(b *grocksdb.WriteBatch) {
		b.Destroy()
	})
	return &batch{
		batch: b,
		db:    db,
	}
}

// Inner returns itself
func (b *batch) Inner() database.Batch { return b }

// NewIterator creates a lexicographically ordered iterator over the database
func (db *Database) NewIterator() database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(nil)
	return &iter{
		it: it,
		db: db,
	}
}

// NewIteratorWithStart creates a lexicographically ordered iterator over the
// database starting at the provided key
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(start)
	return &iter{
		it: it,
		db: db,
	}
}

// NewIteratorWithPrefix creates a lexicographically ordered iterator over the
// database ignoring keys that do not start with the provided prefix
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	it.Seek(prefix)
	return &iter{
		it:            it,
		db:            db,
		expectsPrefix: true,
		prefix:        prefix,
	}
}

// NewIteratorWithStartAndPrefix creates a lexicographically ordered iterator
// over the database starting at start and ignoring keys that do not start with
// the provided prefix
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return &nodb.Iterator{Err: database.ErrClosed}
	case db.corrupted():
		return &nodb.Iterator{Err: database.ErrAvoidCorruption}
	}

	it := db.db.NewIterator(db.iteratorOptions)
	if it == nil {
		return &nodb.Iterator{Err: errFailedToCreateIterator}
	}
	if bytes.Compare(start, prefix) == 1 {
		it.Seek(start)
	} else {
		it.Seek(prefix)
	}
	return &iter{
		it:            it,
		db:            db,
		expectsPrefix: true,
		prefix:        prefix,
	}
}

// Stat returns a particular internal stat of the database.
func (db *Database) Stat(property string) (string, error) {
	return "", database.ErrNotFound
}

// Compact the underlying DB for the given key range.
// Specifically, deleted and overwritten versions are discarded,
// and the data is rearranged to reduce the cost of operations
// needed to access the data. This operation should typically only
// be invoked by users who understand the underlying implementation.
//
// A nil start is treated as a key before all keys in the DB.
// And a nil limit is treated as a key after all keys in the DB.
// Therefore if both are nil then it will compact entire DB.
func (db *Database) Compact(start []byte, limit []byte) error {
	db.lock.RLock()
	defer db.lock.RUnlock()

	switch {
	case db.db == nil:
		return database.ErrClosed
	case db.corrupted():
		return database.ErrAvoidCorruption
	}

	db.db.CompactRange(grocksdb.Range{Start: start, Limit: limit})
	return nil
}

// Close implements the Database interface
func (db *Database) Close() error {
	db.lock.Lock()
	defer db.lock.Unlock()

	if db.db == nil {
		return database.ErrClosed
	}

	db.readOptions.Destroy()
	db.iteratorOptions.Destroy()
	db.writeOptions.Destroy()
	db.db.Close()

	db.db = nil
	return nil
}

func (db *Database) corrupted() bool {
	return atomic.LoadUint64(&db.errored) == 1
}

// batch is a wrapper around a levelDB batch to contain sizes.
type batch struct {
	batch *grocksdb.WriteBatch
	db    *Database
	size  int
}

// Put the value into the batch for later writing
func (b *batch) Put(key, value []byte) error {
	b.batch.Put(key, value)
	b.size += len(key) + len(value) + rocksDBByteOverhead
	return nil
}

// Delete the key during writing
func (b *batch) Delete(key []byte) error {
	b.batch.Delete(key)
	b.size += len(key) + rocksDBByteOverhead
	return nil
}

// Size retrieves the amount of data queued up for writing.
func (b *batch) Size() int { return b.size }

// Write flushes any accumulated data to disk.
func (b *batch) Write() error {
	b.db.lock.RLock()
	defer b.db.lock.RUnlock()

	switch {
	case b.db.db == nil:
		return database.ErrClosed
	case b.db.corrupted():
		return database.ErrAvoidCorruption
	}
	return b.db.db.Write(b.db.writeOptions, b.batch)
}

// Reset resets the batch for reuse.
func (b *batch) Reset() {
	b.batch.Clear()
	b.size = 0
}

// Replay the batch contents.
func (b *batch) Replay(w database.KeyValueWriter) error {
	it := b.batch.NewIterator()
	for it.Next() {
		rec := it.Record()
		switch rec.Type {
		case
			grocksdb.WriteBatchDeletionRecord,
			grocksdb.WriteBatchSingleDeletionRecord:
			if err := w.Delete(rec.Key); err != nil {
				return err
			}
		case grocksdb.WriteBatchValueRecord:
			if err := w.Put(rec.Key, rec.Value); err != nil {
				return err
			}
		}
	}
	return nil
}

type iter struct {
	it            *grocksdb.Iterator
	db            *Database
	expectsPrefix bool
	prefix        []byte
	started       bool
	key           []byte
	value         []byte
}

// Error implements the Iterator interface
func (it *iter) Error() error { return it.it.Err() }

// Key implements the Iterator interface
func (it *iter) Key() []byte {
	return utils.CopyBytes(it.key)
}

// Value implements the Iterator interface
func (it *iter) Value() []byte {
	return utils.CopyBytes(it.value)
}

func (it *iter) Release() {
	it.db.lock.RLock()
	defer it.db.lock.RUnlock()

	if it.db.db != nil {
		it.it.Close()
	}
}

func (it *iter) Next() bool {
	if it.started {
		it.it.Next()
	}
	it.started = true
	valid := it.it.Valid()
	if !valid {
		it.key = nil
		it.value = nil
	} else {
		it.key = it.it.Key().Data()
		it.value = it.it.Value().Data()
		if it.expectsPrefix && !bytes.HasPrefix(it.key, it.prefix) {
			valid = false
			it.key = nil
			it.value = nil
		}
	}
	return valid
}
<|endoftext|>


<commit_before>package api

import (
    "fmt"
    "net/url"
    "strconv"
    "github.com/mitchellh/mapstructure"
)

const (
    DEFAULT_ACTRESS_API_LENGTH = 20
    DEFAULT_ACTRESS_MAX_LENGTH = 100
)

type ActressService struct {
    ApiId        string `mapstructure:"api_id"`
    AffiliateId  string `mapstructure:"affiliate_id"`
    Length       int64  `mapstructure:"hits"`
    Offset       int64  `mapstructure:"offset"`
    Sort         string `mapstructure:"sort"`
    Initial      string `mapstructure:"initial"`
    Keyword      string `mapstructure:"keyword"`
    Bust         string `mapstructure:"bust"`
    Waist        string `mapstructure:"waist"`
    Hip          string `mapstructure:"hip"`
    Height       string `mapstructure:"height"`
    Birthday     string `mapstructure:"birthday"`
}

type ActressRawResponse struct {
    Request ActressService  `mapstructure:"request"`
    Result  ActressResponse `mapstructure:"result"`
}

type ActressResponse struct {
    Id          string             `mapstructure:"id"`
    Name        string             `mapstructure:"name"`
    Ruby        string             `mapstructure:"ruby"`
    Bust        string             `mapstructure:"bust"`
    Waist       string             `mapstructure:"waist"`
    Hip         string             `mapstructure:"hip"`
    Height      string             `mapstructure:"height"`
    Birthday    string             `mapstructure:"birthday"`
    Blood_type  string             `mapstructure:"blood_type"`
    Hobby       string             `mapstructure:"hobby"`
    Prefectures string             `mapstructure:"prefectures"`
    ListURL     ActressProductList `mapstructure:"listURL"`
}

type ActressProductList struct {
    Digital string `mapstructure:"digital"`
    Mono    string `mapstructure:"mono"`
    Monthly string `mapstructure:"monthly"`
    Ppm     string `mapstructure:"ppm"`
    Rental  string `mapstructure:"rental"`
}

func NewActressService(affiliateId, apiId string) *ActressService {
    return &ActressService{
        ApiId:       apiId,
        AffiliateId: affiliateId,
        Length:      DEFAULT_ACTRESS_API_LENGTH,
        Offset:      DEFAULT_API_OFFSET,
        Sort:        "",
        Initial:     "",
        Keyword:     "",
        Bust:        "",
        Waist:       "",
        Hip:         "",
        Height:      "",
        Birthday:    "",

    }
}

func (srv *ActressService) Execute() (*ActressResponse, error) {
    result, err := srv.ExecuteWeak()
    if err != nil {
        return nil, err
    }
    var raw ActressRawResponse
    if err = mapstructure.WeakDecode(result, &raw); err != nil {
        return nil, err
    }
    return &raw.Result, nil
}

func (srv *ActressService) ExecuteWeak() (interface{}, error) {
    reqUrl, err := srv.BuildRequestUrl()
    if err != nil {
        return nil, err
    }

    return RequestJson(reqUrl)
}

func (srv *ActressService) SetLength(length int64) *ActressService {
    srv.Length = length
    return srv
}

func (srv *ActressService) SetHits(length int64) *ActressService {
    srv.SetLength(length)
    return srv
}

func (srv *ActressService) SetOffset(offset int64) *ActressService {
    srv.Offset = offset
    return srv
}

func (srv *ActressService) SetKeyword(keyword string) *ActressService {
    srv.Keyword = TrimString(keyword)
    return srv
}

func (srv *ActressService) SetSort(sort string) *ActressService {
    srv.Sort = TrimString(sort)
    return srv
}

func (srv *ActressService) SetInitial(initial string) *ActressService {
    srv.Initial = TrimString(initial)
    return srv
}

func (srv *ActressService) SetBirthday(birthday string) *ActressService {
    srv.Birthday = TrimString(birthday)
    return srv
}

func (srv *ActressService) SetBust(bust string) *ActressService {
    srv.Bust = TrimString(bust)
    return srv
}

func (srv *ActressService) SetWaist(waist string) *ActressService {
    srv.Waist = TrimString(waist)
    return srv
}

func (srv *ActressService) SetHip(hip string) *ActressService {
    srv.Hip = TrimString(hip)
    return srv
}

func (srv *ActressService) SetHeight(height string) *ActressService {
    srv.Height = TrimString(height)
    return srv
}

func (srv *ActressService) ValidateLength() bool {
    return ValidateRange(srv.Length, 1, DEFAULT_ACTRESS_MAX_LENGTH)
}

func (srv *ActressService) ValidateOffset() bool {
    return srv.Offset >= 1
}

func (srv *ActressService) BuildRequestUrl() (string, error) {
    if srv.ApiId == "" {
        return "", fmt.Errorf("set invalid ApiId parameter.")
    }

    if !ValidateAffiliateId(srv.AffiliateId) {
        return "", fmt.Errorf("set invalid AffiliateId parameter.")
    }

    queries := url.Values{}
    queries.Set("api_id", srv.ApiId)
    queries.Set("affiliate_id", srv.AffiliateId)

    if srv.Length != 0 {
        if !srv.ValidateLength() {
            return "", fmt.Errorf("length out of range: %d", srv.Length)
        }
        queries.Set("hits", strconv.FormatInt(srv.Length, 10))
    }

    if srv.Offset != 0 {
        if !srv.ValidateOffset() {
            return "", fmt.Errorf("offset out of range: %d", srv.Offset)
        }
        queries.Set("offset", strconv.FormatInt(srv.Offset, 10))
    }

    if (srv.Initial != "") {
        queries.Set("initial", srv.Initial)
    }
    if (srv.Sort != "") {
        queries.Set("sort", srv.Sort)
    }
    if (srv.Keyword != "") {
        queries.Set("keyword", srv.Keyword)
    }
    if (srv.Birthday != "") {
        queries.Set("birthday", srv.Birthday)
    }
    if (srv.Bust != "") {
        queries.Set("bust", srv.Bust)
    }
    if (srv.Waist != "") {
        queries.Set("waist", srv.Waist)
    }
    if (srv.Hip != "") {
        queries.Set("hip", srv.Hip)
    }
    if (srv.Height != "") {
        queries.Set("height", srv.Height)
    }

    return API_BASE_URL + "/ActressSearch?" + queries.Encode(), nil
}<commit_msg>fix ActressResponse structure<commit_after>package api

import (
    "fmt"
    "net/url"
    "strconv"
    "github.com/mitchellh/mapstructure"
)

const (
    DEFAULT_ACTRESS_API_LENGTH = 20
    DEFAULT_ACTRESS_MAX_LENGTH = 100
)

type ActressService struct {
    ApiId        string `mapstructure:"api_id"`
    AffiliateId  string `mapstructure:"affiliate_id"`
    Length       int64  `mapstructure:"hits"`
    Offset       int64  `mapstructure:"offset"`
    Sort         string `mapstructure:"sort"`
    Initial      string `mapstructure:"initial"`
    Keyword      string `mapstructure:"keyword"`
    Bust         string `mapstructure:"bust"`
    Waist        string `mapstructure:"waist"`
    Hip          string `mapstructure:"hip"`
    Height       string `mapstructure:"height"`
    Birthday     string `mapstructure:"birthday"`
}

type ActressRawResponse struct {
    Request ActressService  `mapstructure:"request"`
    Result  ActressResponse `mapstructure:"result"`
}

type ActressResponse struct {
    ResultCount   int64     `mapstructure:"result_count"`
    TotalCount    int64     `mapstructure:"total_count"`
    FirstPosition int64     `mapstructure:"first_position"`
    Actresses     []Actress `mapstructure:"actress"`
}

type Actress struct {
    Id          string             `mapstructure:"id"`
    Name        string             `mapstructure:"name"`
    Ruby        string             `mapstructure:"ruby"`
    Bust        string             `mapstructure:"bust"`
    Waist       string             `mapstructure:"waist"`
    Hip         string             `mapstructure:"hip"`
    Height      string             `mapstructure:"height"`
    Birthday    string             `mapstructure:"birthday"`
    Blood_type  string             `mapstructure:"blood_type"`
    Hobby       string             `mapstructure:"hobby"`
    Prefectures string             `mapstructure:"prefectures"`
    ListURL     ActressProductList `mapstructure:"listURL"`
}

type ActressProductList struct {
    Digital string `mapstructure:"digital"`
    Mono    string `mapstructure:"mono"`
    Monthly string `mapstructure:"monthly"`
    Ppm     string `mapstructure:"ppm"`
    Rental  string `mapstructure:"rental"`
}

func NewActressService(affiliateId, apiId string) *ActressService {
    return &ActressService{
        ApiId:       apiId,
        AffiliateId: affiliateId,
        Length:      DEFAULT_ACTRESS_API_LENGTH,
        Offset:      DEFAULT_API_OFFSET,
        Sort:        "",
        Initial:     "",
        Keyword:     "",
        Bust:        "",
        Waist:       "",
        Hip:         "",
        Height:      "",
        Birthday:    "",

    }
}

func (srv *ActressService) Execute() (*ActressResponse, error) {
    result, err := srv.ExecuteWeak()
    if err != nil {
        return nil, err
    }
    var raw ActressRawResponse
    if err = mapstructure.WeakDecode(result, &raw); err != nil {
        return nil, err
    }
    return &raw.Result, nil
}

func (srv *ActressService) ExecuteWeak() (interface{}, error) {
    reqUrl, err := srv.BuildRequestUrl()
    if err != nil {
        return nil, err
    }

    return RequestJson(reqUrl)
}

func (srv *ActressService) SetLength(length int64) *ActressService {
    srv.Length = length
    return srv
}

func (srv *ActressService) SetHits(length int64) *ActressService {
    srv.SetLength(length)
    return srv
}

func (srv *ActressService) SetOffset(offset int64) *ActressService {
    srv.Offset = offset
    return srv
}

func (srv *ActressService) SetKeyword(keyword string) *ActressService {
    srv.Keyword = TrimString(keyword)
    return srv
}

func (srv *ActressService) SetSort(sort string) *ActressService {
    srv.Sort = TrimString(sort)
    return srv
}

func (srv *ActressService) SetInitial(initial string) *ActressService {
    srv.Initial = TrimString(initial)
    return srv
}

func (srv *ActressService) SetBirthday(birthday string) *ActressService {
    srv.Birthday = TrimString(birthday)
    return srv
}

func (srv *ActressService) SetBust(bust string) *ActressService {
    srv.Bust = TrimString(bust)
    return srv
}

func (srv *ActressService) SetWaist(waist string) *ActressService {
    srv.Waist = TrimString(waist)
    return srv
}

func (srv *ActressService) SetHip(hip string) *ActressService {
    srv.Hip = TrimString(hip)
    return srv
}

func (srv *ActressService) SetHeight(height string) *ActressService {
    srv.Height = TrimString(height)
    return srv
}

func (srv *ActressService) ValidateLength() bool {
    return ValidateRange(srv.Length, 1, DEFAULT_ACTRESS_MAX_LENGTH)
}

func (srv *ActressService) ValidateOffset() bool {
    return srv.Offset >= 1
}

func (srv *ActressService) BuildRequestUrl() (string, error) {
    if srv.ApiId == "" {
        return "", fmt.Errorf("set invalid ApiId parameter.")
    }

    if !ValidateAffiliateId(srv.AffiliateId) {
        return "", fmt.Errorf("set invalid AffiliateId parameter.")
    }

    queries := url.Values{}
    queries.Set("api_id", srv.ApiId)
    queries.Set("affiliate_id", srv.AffiliateId)

    if srv.Length != 0 {
        if !srv.ValidateLength() {
            return "", fmt.Errorf("length out of range: %d", srv.Length)
        }
        queries.Set("hits", strconv.FormatInt(srv.Length, 10))
    }

    if srv.Offset != 0 {
        if !srv.ValidateOffset() {
            return "", fmt.Errorf("offset out of range: %d", srv.Offset)
        }
        queries.Set("offset", strconv.FormatInt(srv.Offset, 10))
    }

    if (srv.Initial != "") {
        queries.Set("initial", srv.Initial)
    }
    if (srv.Sort != "") {
        queries.Set("sort", srv.Sort)
    }
    if (srv.Keyword != "") {
        queries.Set("keyword", srv.Keyword)
    }
    if (srv.Birthday != "") {
        queries.Set("birthday", srv.Birthday)
    }
    if (srv.Bust != "") {
        queries.Set("bust", srv.Bust)
    }
    if (srv.Waist != "") {
        queries.Set("waist", srv.Waist)
    }
    if (srv.Hip != "") {
        queries.Set("hip", srv.Hip)
    }
    if (srv.Height != "") {
        queries.Set("height", srv.Height)
    }

    return API_BASE_URL + "/ActressSearch?" + queries.Encode(), nil
}<|endoftext|>


<commit_before>// Copyright 2013 tsuru 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 main

import (
	"fmt"
	"github.com/globocom/tsuru/auth"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"net/http"
)

const (
	tsuruMin = "0.7.2"
	craneMin = "0.4"
)

func setVersionHeaders(w http.ResponseWriter) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
}

type handler func(http.ResponseWriter, *http.Request) error

func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	if err := fn(&fw, r); err != nil {
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), http.StatusInternalServerError)
		}
		log.Print(err)
	}
}

func validate(token string, r *http.Request) (*auth.Token, error) {
	if token == "" {
		return nil, &errors.Http{
			Message: "You must provide the Authorization header",
		}
	}
	invalid := &errors.Http{Message: "Invalid token"}
	t, err := auth.GetToken(token)
	if err != nil {
		return nil, invalid
	}
	if t.AppName != "" {
		if q := r.URL.Query().Get(":app"); q != "" && t.AppName != q {
			return nil, invalid
		}
	}
	return t, nil
}

type authorizationRequiredHandler func(http.ResponseWriter, *http.Request, *auth.Token) error

func (fn authorizationRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if t, err := validate(token, r); err != nil {
		http.Error(&fw, err.Error(), http.StatusUnauthorized)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}

type adminRequiredHandler authorizationRequiredHandler

func (fn adminRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if token == "" {
		http.Error(&fw, "You must provide the Authorization header", http.StatusUnauthorized)
	} else if t, err := auth.GetToken(token); err != nil {
		http.Error(&fw, "Invalid token", http.StatusUnauthorized)
	} else if user, err := t.User(); err != nil || !user.IsAdmin() {
		http.Error(&fw, "Forbidden", http.StatusForbidden)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}
<commit_msg>api: request tsuru version 0.7.9<commit_after>// Copyright 2013 tsuru 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 main

import (
	"fmt"
	"github.com/globocom/tsuru/auth"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"net/http"
)

const (
	tsuruMin = "0.7.9"
	craneMin = "0.4"
)

func setVersionHeaders(w http.ResponseWriter) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
}

type handler func(http.ResponseWriter, *http.Request) error

func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	if err := fn(&fw, r); err != nil {
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), http.StatusInternalServerError)
		}
		log.Print(err)
	}
}

func validate(token string, r *http.Request) (*auth.Token, error) {
	if token == "" {
		return nil, &errors.Http{
			Message: "You must provide the Authorization header",
		}
	}
	invalid := &errors.Http{Message: "Invalid token"}
	t, err := auth.GetToken(token)
	if err != nil {
		return nil, invalid
	}
	if t.AppName != "" {
		if q := r.URL.Query().Get(":app"); q != "" && t.AppName != q {
			return nil, invalid
		}
	}
	return t, nil
}

type authorizationRequiredHandler func(http.ResponseWriter, *http.Request, *auth.Token) error

func (fn authorizationRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if t, err := validate(token, r); err != nil {
		http.Error(&fw, err.Error(), http.StatusUnauthorized)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}

type adminRequiredHandler authorizationRequiredHandler

func (fn adminRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if token == "" {
		http.Error(&fw, "You must provide the Authorization header", http.StatusUnauthorized)
	} else if t, err := auth.GetToken(token); err != nil {
		http.Error(&fw, "Invalid token", http.StatusUnauthorized)
	} else if user, err := t.User(); err != nil || !user.IsAdmin() {
		http.Error(&fw, "Forbidden", http.StatusForbidden)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"polydawn.net/hitch/api/rdef"
)

type (
	CatalogName string // oft like "project.org/thing".  The first part of an identifying triple.
	ReleaseName string // oft like "1.8".  The second part of an identifying triple.
	ItemLabel   string // oft like "linux-amd64" or "docs".  The third part of an identifying triple.
)

/*
	A Catalog is the accumulated releases for a particular piece of software.

	A Catalog indicates a single author.  When observing new releases and/or
	metadata updates in a Catalog over time, you should expect to see it signed
	by the same key.  (Signing is not currently a built-in operation of `hitch`,
	but may be added in future releases.)
*/
type Catalog struct {
	// Name of self.
	Name CatalogName

	// Ordered list of release entries.
	// Order not particularly important, though UIs generally display in this order.
	// Most recent entries are typically placed at the top (e.g. index zero).
	//
	// Each entry must have a unique ReleaseName in the scope of its Catalog.
	Releases []ReleaseEntry
}

type ReleaseEntry struct {
	Name     ReleaseName
	Items    map[ItemLabel]rdef.WareID
	Metadata map[string]string
	Hazards  map[string]string
	Replay   *Replay
}

type Replay struct {
	Computations map[CommissionName]Commission // review: this could still be `map[StepName]rdef.SetupHash`, because reminder, we're not a planner; this is replay not pipeline.
	Products     map[ItemLabel]struct {        // n.b. since this is final outputs only, implied here is commissions can also use an input "wire:<commissionName>:<outputPath>" for intermediates.
		CommissionName CommissionName
		OutputSlot     rdef.AbsPath
	}
	RunRecords map[*rdef.RunRecord]struct{} // runRecords map themselves back to SetupHash, so this is just a bag.  It is forbidden to have runrecords that a product or intermediate wire point to must... wait.
}
<commit_msg>api: Fix regarding that last epiphany.<commit_after>package api

import (
	"polydawn.net/hitch/api/rdef"
)

type (
	CatalogName string // oft like "project.org/thing".  The first part of an identifying triple.
	ReleaseName string // oft like "1.8".  The second part of an identifying triple.
	ItemLabel   string // oft like "linux-amd64" or "docs".  The third part of an identifying triple.
)

/*
	A Catalog is the accumulated releases for a particular piece of software.

	A Catalog indicates a single author.  When observing new releases and/or
	metadata updates in a Catalog over time, you should expect to see it signed
	by the same key.  (Signing is not currently a built-in operation of `hitch`,
	but may be added in future releases.)
*/
type Catalog struct {
	// Name of self.
	Name CatalogName

	// Ordered list of release entries.
	// Order not particularly important, though UIs generally display in this order.
	// Most recent entries are typically placed at the top (e.g. index zero).
	//
	// Each entry must have a unique ReleaseName in the scope of its Catalog.
	Releases []ReleaseEntry
}

type ReleaseEntry struct {
	Name     ReleaseName
	Items    map[ItemLabel]rdef.WareID
	Metadata map[string]string
	Hazards  map[string]string
	Replay   *Replay
}

type Replay struct {
	Computations map[CommissionName]Commission // review: this could still be `map[StepName]rdef.SetupHash`, because reminder, we're not a planner; this is replay not pipeline.
	Products     map[ItemLabel]struct {        // n.b. since this is final outputs only, implied here is commissions can also use an input "wire:<commissionName>:<outputPath>" for intermediates.
		CommissionName CommissionName
		OutputSlot     rdef.AbsPath
	}
	RunRecords map[CommissionName]map[rdef.RunRecordHash]*rdef.RunRecord // runRecords are stored indexed per step.  It is forbidden to have two runrecords for the same step with conflicting outputs IFF that output slot is referenced by either the final Products or any intermediate Wire.
}
<|endoftext|>


<commit_before>package godingtalk

//SendAppMessage is 发送企业会话消息
func (c *DingTalkClient) SendAppMessage(agentID string, touser string, msg string) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"touser":  touser,
		"agentid": agentID,
		"msgtype": "text",
		"text": map[string]interface{}{
			"content": msg,
		},
	}
	err := c.httpRPC("message/send", nil, request, &data)
	return err
}

//SendTextMessage is 发送普通文本消息
func (c *DingTalkClient) SendTextMessage(sender string, cid string, msg string) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"chatid":  cid,
		"sender":  sender,
		"msgtype": "text",
		"text": map[string]interface{}{
			"content": msg,
		},
	}
	err := c.httpRPC("chat/send", nil, request, &data)
	return err
}

//OAMessage is the Message for OA
type OAMessage struct {
	URL  string `json:"message_url"`
	Head struct {
		BgColor string `json:"bgcolor,omitempty"`
		Text    string `json:"text,omitempty"`
	} `json:"head,omitempty"`
	Body struct {
		Title string `json:"title,omitempty"`
		Form  []struct {
			Key   string `json:"key,omitempty"`
			Value string `json:"value,omitempty"`
		} `json:"body,omitempty"`
		Rich struct {
			Num  string `json:"num,omitempty"`
			Unit string `json:"body,omitempty"`
		} `json:"rich,omitempty"`
		Content string `json:"content,omitempty"`
	} `json:"body,omitempty"`
	Image     string `json:"image,omitempty"`
	FileCount int    `json:"file_count,omitempty"`
	Author    string `json:"author,omitempty"`
}

//SendOAMessage is 发送OA消息
func (c *DingTalkClient) SendOAMessage(sender string, cid string, msg OAMessage) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"chatid":  cid,
		"sender":  sender,
		"msgtype": "oa",
		"oa":      msg,
	}
	err := c.httpRPC("chat/send", nil, request, &data)
	return err
}
<commit_msg>Fixed OAMessage struct<commit_after>package godingtalk

//SendAppMessage is 发送企业会话消息
func (c *DingTalkClient) SendAppMessage(agentID string, touser string, msg string) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"touser":  touser,
		"agentid": agentID,
		"msgtype": "text",
		"text": map[string]interface{}{
			"content": msg,
		},
	}
	err := c.httpRPC("message/send", nil, request, &data)
	return err
}

//SendTextMessage is 发送普通文本消息
func (c *DingTalkClient) SendTextMessage(sender string, cid string, msg string) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"chatid":  cid,
		"sender":  sender,
		"msgtype": "text",
		"text": map[string]interface{}{
			"content": msg,
		},
	}
	err := c.httpRPC("chat/send", nil, request, &data)
	return err
}

//OAMessage is the Message for OA
type OAMessage struct {
	URL  string `json:"message_url"`
	Head struct {
		BgColor string `json:"bgcolor,omitempty"`
		Text    string `json:"text,omitempty"`
	} `json:"head,omitempty"`
	Body struct {
		Title string `json:"title,omitempty"`
		Form  []struct {
			Key   string `json:"key,omitempty"`
			Value string `json:"value,omitempty"`
		} `json:"form,omitempty"`
		Rich struct {
			Num  string `json:"num,omitempty"`
			Unit string `json:"body,omitempty"`
		} `json:"rich,omitempty"`
		Content   string `json:"content,omitempty"`
		Image     string `json:"image,omitempty"`
		FileCount int    `json:"file_count,omitempty"`
		Author    string `json:"author,omitempty"`
	} `json:"body,omitempty"`
}

//SendOAMessage is 发送OA消息
func (c *DingTalkClient) SendOAMessage(sender string, cid string, msg OAMessage) error {
	var data OAPIResponse
	request := map[string]interface{}{
		"chatid":  cid,
		"sender":  sender,
		"msgtype": "oa",
		"oa":      msg,
	}
	err := c.httpRPC("chat/send", nil, request, &data)
	return err
}
<|endoftext|>


<commit_before>// Package pydiomiddleware contains the logic for a middleware directive (repetitive task done for a Pydio request)
/*
 * Copyright 2007-2016 Abstrium <contact (at) pydio.com>
 * This file is part of Pydio.
 *
 * Pydio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Pydio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Pydio.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <https://pydio.com/>.
 */
package pydiomiddleware

import (
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/mholt/caddy/caddyhttp/httpserver"
	pydhttp "github.com/pydio/pydio-booster/http"
	pydioworker "github.com/pydio/pydio-booster/worker"
)

var client = pydhttp.NewClient()

// RequestJob definition for the uploader
type RequestJob struct {
	Request    http.Request
	HandleFunc func(io.Reader) error
	ErrorFunc  func()
}

// Do the job
func (j *RequestJob) Do() (err error) {

	resp, err := client.Do(&j.Request)
	if err != nil {
		j.ErrorFunc()
		return
	}

	if resp.Body != nil {
		defer resp.Body.Close()
	}

	if err != nil && err != io.EOF {
		j.ErrorFunc()
		return
	}

	if resp.StatusCode != http.StatusOK || err != nil {
		logger.Errorf("Not authorized : %v %v", j.Request, resp)
		j.ErrorFunc()
		return err
	}

	return j.HandleFunc(resp.Body)
}

// NewRequestJob prepares the job for the middleware request
// based on the rules
func NewRequestJob(
	u url.URL,
	headers [][2]string,
	cookies []*http.Cookie,
	out string,
	ctx context.Context,
	replacer httpserver.Replacer,
	encoder json.Encoder,
	writer io.Writer,
	close func() error,
	cancel func(),
) (pydioworker.Job, error) {

	queryArgs := u.Query()
	logger.Infoln("Request Job Start", u, headers, queryArgs, out)

	node, err := getContextNode(ctx)
	if err == nil {
		logger.Debugln("Request : Retrieved node ", node.Repo)
		var repo *url.URL
		var dir *url.URL

		repo = &url.URL{Path: node.Repo.String()}
		dir = &url.URL{Path: strings.TrimPrefix(node.Dir.String(), "/")}

		replacer.Set("repo", repo.String())
		replacer.Set("nodedir", dir.String())
		replacer.Set("nodename", node.Basename)

		logger.Debugln("Request : Replacer is set ", replacer)
	} else {
		logger.Errorln("Request : Could not read node")
	}

	u.Path = replacer.Replace(u.Path)

	values := u.Query()
	if out != "token" {
		if auth, errAuth := getContextAuthParams(ctx, u.Path); errAuth == nil {
			values.Set("auth_hash", auth.Hash)
			values.Set("auth_token", auth.Token)
			if auth.Key != "" {
				values.Set("key", auth.Key)
			}
		}
	}
	u.RawQuery = values.Encode()

	request, _ := http.NewRequest("GET", u.String(), nil)
	logger.Debugln("Doing headers")
	for _, header := range headers {
		request.Header.Add(header[0], replacer.Replace(header[1]))
	}

	logger.Debugln("Doing cookies")
	for _, cookie := range cookies {
		request.AddCookie(cookie)
	}

	request.URL = &u

	logger.Debugf("URL is %s - headers - %v cookies - %v", u.String(), headers, request.Cookies())

	job := &RequestJob{
		Request:   *request,
		ErrorFunc: cancel,
		HandleFunc: func(r io.Reader) error {
			defer close()

			var q query
			var dec decoder

			logger.Debugln("Out to ", out)

			switch out {
			case "body":
				// Write response header
				//writeHeader(w, resp)

				// Write the response body
				//data, _ := ioutil.ReadAll(r)
				//log.Printf("%s", data)

				_, err = io.Copy(writer, r)
				if err != nil {
					logger.Errorf("Could not write body %v", err)
					return err
				}

				return nil

			case "user":
				q = &UserQuery{}
				dec = xml.NewDecoder(r)

			case "options":
				q = &OptionsQuery{}
				dec = json.NewDecoder(r)

			case "proxy":
				q = &ProxyQuery{}
				dec = json.NewDecoder(r)

			case "token":
				q = &TokenQuery{}
				dec = json.NewDecoder(r)

			case "client":
				q = &ClientQuery{}
				dec = json.NewDecoder(r)

			default:
				return errors.New("[ERROR:MW] Wrong decoding type")
			}

			// DEBUG Display data
			// data, _ := ioutil.ReadAll(r)
			// log.Println("Data received ", data)

			if err := dec.Decode(q); err == io.EOF {
				logger.Debugln("End of decoding")
			} else if err != nil {
				logger.Errorln("Request: error while decoding ", err)
				cancel()
			} else {
				logger.Debugln("Done with success ", q)
			}

			if user, ok := q.(*UserQuery); ok {
				encoder.Encode(user.User)
			}
			if options, ok := q.(*OptionsQuery); ok {
				encoder.Encode(options)
			}
			if proxy, ok := q.(*ProxyQuery); ok {
				encoder.Encode(proxy)
			}
			if token, ok := q.(*TokenQuery); ok {
				encoder.Encode(token)
			}
			if client, ok := q.(*ClientQuery); ok {
				encoder.Encode(client)
			}

			return nil
		},
	}

	return pydioworker.Job(job), nil
}

type decoder interface {
	Decode(v interface{}) error
}
<commit_msg>Fix to #1911<commit_after>// Package pydiomiddleware contains the logic for a middleware directive (repetitive task done for a Pydio request)
/*
 * Copyright 2007-2016 Abstrium <contact (at) pydio.com>
 * This file is part of Pydio.
 *
 * Pydio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Pydio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Pydio.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <https://pydio.com/>.
 */
package pydiomiddleware

import (
	"context"
	"encoding/json"
	"encoding/xml"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/mholt/caddy/caddyhttp/httpserver"
	pydhttp "github.com/pydio/pydio-booster/http"
	"github.com/pydio/pydio-booster/log"
	pydioworker "github.com/pydio/pydio-booster/worker"
)

var client = pydhttp.NewClient()

// RequestJob definition for the uploader
type RequestJob struct {
	Request    http.Request
	HandleFunc func(io.Reader) error
	ErrorFunc  func()
}

// Do the job
func (j *RequestJob) Do() (err error) {

	resp, err := client.Do(&j.Request)
	if err != nil {
		j.ErrorFunc()
		return
	}

	if resp.Body != nil {
		defer resp.Body.Close()
	}

	if err != nil && err != io.EOF {
		j.ErrorFunc()
		return
	}

	if resp.StatusCode != http.StatusOK || err != nil {
		logger.Errorf("Not authorized : %v %v", j.Request, resp)
		j.ErrorFunc()
		return err
	}

	return j.HandleFunc(resp.Body)
}

// NewRequestJob prepares the job for the middleware request
// based on the rules
func NewRequestJob(
	u url.URL,
	headers [][2]string,
	cookies []*http.Cookie,
	out string,
	ctx context.Context,
	replacer httpserver.Replacer,
	encoder json.Encoder,
	writer io.Writer,
	close func() error,
	cancel func(),
) (pydioworker.Job, error) {

	queryArgs := u.Query()
	logger.Infoln("Request Job Start", u, headers, queryArgs, out)

	node, err := getContextNode(ctx)
	if err == nil {
		logger.Debugln("Request : Retrieved node ", node.Repo)
		var repo *url.URL
		var dir *url.URL

		repo = &url.URL{Opaque: node.Repo.String()}
		dir = &url.URL{Opaque: strings.TrimPrefix(node.Dir.String(), "/")}

		replacer.Set("repo", repo.String())
		replacer.Set("nodedir", dir.String())
		replacer.Set("nodename", node.Basename)

		logger.Debugln("Request : Replacer is set ", replacer)
	} else {
		logger.Errorln("Request : Could not read node")
	}

	u.Path = replacer.Replace(u.Path)

	values := url.Values{}
	for arg, vals := range u.Query() {
		for _, val := range vals {
			log.Debugln(replacer.Replace(val))
			values.Add(arg, replacer.Replace(val))
		}
	}

	if len(cookies) == 0 {
		if auth, errAuth := getContextAuthParams(ctx, u.Path); errAuth == nil {
			values.Set("auth_hash", auth.Hash)
			values.Set("auth_token", auth.Token)
			if auth.Key != "" {
				values.Set("key", auth.Key)
			}
		}
	}
	u.RawQuery = values.Encode()

	request, _ := http.NewRequest("GET", u.String(), nil)
	logger.Debugln("Doing headers")
	for _, header := range headers {
		request.Header.Add(header[0], replacer.Replace(header[1]))
	}

	logger.Debugln("Doing cookies")
	for _, cookie := range cookies {
		request.AddCookie(cookie)
	}

	request.URL = &u

	logger.Debugf("URL is %s - headers - %v cookies - %v", u.String(), headers, request.Cookies())

	job := &RequestJob{
		Request:   *request,
		ErrorFunc: cancel,
		HandleFunc: func(r io.Reader) error {
			defer close()

			var q query
			var dec decoder

			logger.Debugln("Out to ", out)

			switch out {
			case "body":
				// Write response header
				//writeHeader(w, resp)

				// Write the response body
				//data, _ := ioutil.ReadAll(r)
				//log.Printf("%s", data)

				_, err = io.Copy(writer, r)
				if err != nil {
					logger.Errorf("Could not write body %v", err)
					return err
				}

				return nil

			case "user":
				q = &UserQuery{}
				dec = xml.NewDecoder(r)

			case "options":
				q = &OptionsQuery{}
				dec = json.NewDecoder(r)

			case "proxy":
				q = &ProxyQuery{}
				dec = json.NewDecoder(r)

			case "token":
				q = &TokenQuery{}
				dec = json.NewDecoder(r)

			case "client":
				q = &ClientQuery{}
				dec = json.NewDecoder(r)

			default:
				return errors.New("[ERROR:MW] Wrong decoding type")
			}

			// DEBUG Display data
			// data, _ := ioutil.ReadAll(r)
			// log.Println("Data received ", data)

			if err := dec.Decode(q); err == io.EOF {
				logger.Debugln("End of decoding")
			} else if err != nil {
				logger.Errorln("Request: error while decoding ", err)
				cancel()
			} else {
				logger.Debugln("Done with success ", q)
			}

			if user, ok := q.(*UserQuery); ok {
				encoder.Encode(user.User)
			}
			if options, ok := q.(*OptionsQuery); ok {
				encoder.Encode(options)
			}
			if proxy, ok := q.(*ProxyQuery); ok {
				encoder.Encode(proxy)
			}
			if token, ok := q.(*TokenQuery); ok {
				encoder.Encode(token)
			}
			if client, ok := q.(*ClientQuery); ok {
				encoder.Encode(client)
			}

			return nil
		},
	}

	return pydioworker.Job(job), nil
}

type decoder interface {
	Decode(v interface{}) error
}
<|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the data service.
*/
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"log"
	"os"
	"path/filepath"
	"pkg.monsti.org/service"
	"pkg.monsti.org/util"
	"sync"
)

// DataService implements RPC methods for the Data service.
type DataService struct {
	Info     *service.InfoClient
	Settings settings
}

// getNode looks up the given node.
// If no such node exists, return nil.
func getNode(root, path string) (node *service.NodeInfo, err error) {
	node_path := filepath.Join(root, path[1:], "node.yaml")
	content, err := ioutil.ReadFile(node_path)
	if err != nil {
		return
	}
	if err = goyaml.Unmarshal(content, &node); err != nil {
		node = nil
		return
	}
	node.Path = path
	return
}

// getChildren looks up child nodes of the given node.
func getChildren(root, path string) (nodes []service.NodeInfo, err error) {
	files, err := ioutil.ReadDir(filepath.Join(root, path))
	if err != nil {
		return
	}
	for _, file := range files {
		if !file.IsDir() {
			continue
		}
		node, _ := getNode(root, filepath.Join(path, file.Name()))
		if node != nil {
			nodes = append(nodes, *node)
		}
	}
	return
}

type GetChildrenArgs struct {
	Site, Node string
}

func (i *DataService) GetChildren(args GetChildrenArgs,
	reply *[]service.NodeInfo) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	ret, err := getChildren(site, args.Node[1:])
	*reply = ret
	return err
}

func (i *DataService) GetNodeData(args *service.GetNodeDataArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	ret, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		*reply = nil
		return nil
	}
	*reply = ret
	return err
}

func (i *DataService) WriteNodeData(args *service.WriteNodeDataArgs,
	reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	err := ioutil.WriteFile(path, []byte(args.Content), 0600)
	return err
}

func (i *DataService) UpdateNode(args *service.UpdateNodeArgs, reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	return writeNode(args.Node, site)
}

type RemoveNodeArgs struct {
	Site, Node string
}

func (i *DataService) RemoveNode(args *RemoveNodeArgs, reply *int) error {
	root := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	nodePath := filepath.Join(root, args.Node[1:])
	if err := os.RemoveAll(nodePath); err != nil {
		return fmt.Errorf("Can't remove node: %v", err)
	}
	return nil
}

// writeNode writes the given node to the data directory located at the given
// root.
func writeNode(reqnode service.NodeInfo, root string) error {
	path := reqnode.Path
	reqnode.Path = ""
	content, err := goyaml.Marshal(&reqnode)
	if err != nil {
		return err
	}
	node_path := filepath.Join(root, path[1:],
		"node.yaml")
	if err := os.Mkdir(filepath.Dir(node_path), 0700); err != nil {
		if !os.IsExist(err) {
			panic("Can't create directory for new node: " + err.Error())
		}
	}
	return ioutil.WriteFile(node_path, content, 0600)
}

type settings struct {
	Monsti util.MonstiSettings
}

func main() {
	logger := log.New(os.Stderr, "data ", log.LstdFlags)

	// Load configuration
	flag.Parse()
	cfgPath := util.GetConfigPath(flag.Arg(0))
	var settings settings
	if err := util.LoadModuleSettings("data", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to Info service: %v", err)
	}

	// Start own Data service
	var waitGroup sync.WaitGroup
	logger.Println("Starting Data service")
	waitGroup.Add(1)
	dataPath := settings.Monsti.GetServicePath(service.Data.String())
	go func() {
		defer waitGroup.Done()
		var provider service.Provider
		var data_ DataService
		data_.Info = info
		data_.Settings = settings
		provider.Logger = logger
		if err := provider.Serve(dataPath, "Data", &data_); err != nil {
			logger.Fatalf("Could not start Data service: %v", err)
		}
	}()

	if err := info.PublishService("Data", dataPath); err != nil {
		logger.Fatalf("Could not publish Data service: %v", err)
	}

	waitGroup.Wait()
}
<commit_msg>Implement GetNode<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the data service.
*/
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"log"
	"os"
	"path/filepath"
	"pkg.monsti.org/service"
	"pkg.monsti.org/util"
	"sync"
)

// DataService implements RPC methods for the Data service.
type DataService struct {
	Info     *service.InfoClient
	Settings settings
}

// getNode looks up the given node.
// If no such node exists, return nil.
func getNode(root, path string) (node *service.NodeInfo, err error) {
	node_path := filepath.Join(root, path[1:], "node.yaml")
	content, err := ioutil.ReadFile(node_path)
	if err != nil {
		return
	}
	if err = goyaml.Unmarshal(content, &node); err != nil {
		node = nil
		return
	}
	node.Path = path
	return
}

type GetNodeArgs struct{Site, Path string}

func (i *DataService) GetNode(args *GetNodeArgs,
	reply *service.NodeInfo) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	fmt.Println(site, args.Path)
	node, err := getNode(site, args.Path)
	if err != nil {
		reply = nil
		return err
	}
	*reply = *node
	return nil
}

// getChildren looks up child nodes of the given node.
func getChildren(root, path string) (nodes []service.NodeInfo, err error) {
	files, err := ioutil.ReadDir(filepath.Join(root, path))
	if err != nil {
		return
	}
	for _, file := range files {
		if !file.IsDir() {
			continue
		}
		node, _ := getNode(root, filepath.Join(path, file.Name()))
		if node != nil {
			nodes = append(nodes, *node)
		}
	}
	return
}

type GetChildrenArgs struct {
	Site, Node string
}

func (i *DataService) GetChildren(args GetChildrenArgs,
	reply *[]service.NodeInfo) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	ret, err := getChildren(site, args.Node[1:])
	*reply = ret
	return err
}

func (i *DataService) GetNodeData(args *service.GetNodeDataArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	ret, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		*reply = nil
		return nil
	}
	*reply = ret
	return err
}

func (i *DataService) WriteNodeData(args *service.WriteNodeDataArgs,
	reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	err := ioutil.WriteFile(path, []byte(args.Content), 0600)
	return err
}

func (i *DataService) UpdateNode(args *service.UpdateNodeArgs, reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	return writeNode(args.Node, site)
}

type RemoveNodeArgs struct {
	Site, Node string
}

func (i *DataService) RemoveNode(args *RemoveNodeArgs, reply *int) error {
	root := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	nodePath := filepath.Join(root, args.Node[1:])
	if err := os.RemoveAll(nodePath); err != nil {
		return fmt.Errorf("Can't remove node: %v", err)
	}
	return nil
}

// writeNode writes the given node to the data directory located at the given
// root.
func writeNode(reqnode service.NodeInfo, root string) error {
	path := reqnode.Path
	reqnode.Path = ""
	content, err := goyaml.Marshal(&reqnode)
	if err != nil {
		return err
	}
	node_path := filepath.Join(root, path[1:],
		"node.yaml")
	if err := os.Mkdir(filepath.Dir(node_path), 0700); err != nil {
		if !os.IsExist(err) {
			panic("Can't create directory for new node: " + err.Error())
		}
	}
	return ioutil.WriteFile(node_path, content, 0600)
}

type settings struct {
	Monsti util.MonstiSettings
}

func main() {
	logger := log.New(os.Stderr, "data ", log.LstdFlags)

	// Load configuration
	flag.Parse()
	cfgPath := util.GetConfigPath(flag.Arg(0))
	var settings settings
	if err := util.LoadModuleSettings("data", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to Info service: %v", err)
	}

	// Start own Data service
	var waitGroup sync.WaitGroup
	logger.Println("Starting Data service")
	waitGroup.Add(1)
	dataPath := settings.Monsti.GetServicePath(service.Data.String())
	go func() {
		defer waitGroup.Done()
		var provider service.Provider
		var data_ DataService
		data_.Info = info
		data_.Settings = settings
		provider.Logger = logger
		if err := provider.Serve(dataPath, "Data", &data_); err != nil {
			logger.Fatalf("Could not start Data service: %v", err)
		}
	}()

	if err := info.PublishService("Data", dataPath); err != nil {
		logger.Fatalf("Could not publish Data service: %v", err)
	}

	waitGroup.Wait()
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package msg

import (
	"net"
	"path"
	"strings"

	"github.com/miekg/dns"
)

// This *is* the rdata from a SRV record, but with a twist.
// Host (Target in SRV) must be a domain name, but if it looks like an IP
// address (4/6), we will treat it like an IP address.
type Service struct {
	Host     string `json:"host,omitempty"`
	Port     int    `json:"port,omitempty"`
	Priority int    `json:"priority,omitempty"`
	Weight   int    `json:"weight,omitempty"`
	Text     string `json:"text,omitempty"`
	Ttl      uint32 `json:"ttl,omitempty"`

	// When a SRV record with a "Host: IP-address" is added, we synthesize a srv.Target
	// domain name.
	// Normally we convert the full Key where the record lives to a DNS name and use
	// this as the srv.Target.
	// When TargetStrip > 0 we strip the left most TargetStrip labels from the DNS name.
	TargetStrip int `json:"targetstrip",omitempty"`

	// Etcd key where we found this service and ignored from json un-/marshalling
	Key string `json:"-"`
}

// NewSRV returns a new SRV record based on the Service.
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
	host := dns.Fqdn(s.Host)

	offset, end := 0, false
	for i := 0; i < s.TargetStrip; i++ {
		offset, end = dns.NextLabel(host, offset)
	}
	if end {
		// We overshot the name, use the orignal one.
		offset = 0
	}
	host = host[offset:]

	return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
		Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host}
}

// NewA returns a new A record based on the Service.
func (s *Service) NewA(name string, ip net.IP) *dns.A {
	return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
}

// NewAAAA returns a new AAAA record based on the Service.
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
	return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
}

// NewCNAME returns a new CNAME record based on the Service.
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
	return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: target}
}

// NewNS returns a new NS record based on the Service.
func (s *Service) NewNS(name string, target string) *dns.NS {
	return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: target}
}

// NewTXT returns a new TXT record based on the Service.
func (s *Service) NewTXT(name string) *dns.TXT {
	return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)}
}

// NewPTR returns a new PTR record based on the Service.
func (s *Service) NewPTR(name string, ttl uint32) *dns.PTR {
	return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}, Ptr: dns.Fqdn(s.Host)}
}

// As Path, but
// if a name contains wildcards (*), the name will be chopped of before the (first) wildcard, and
// we do a highler evel search and later find the matching names.
// So service.*.skydns.local, will look for all services under skydns.local and will later check
// for names that match service.*.skydns.local.  If a wildcard is found the returned bool is true.
func PathWithWildcard(s string) (string, bool) {
	l := dns.SplitDomainName(s)
	for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	for i, k := range l {
		if k == "*" {
			return path.Join(append([]string{"/skydns/"}, l[:i]...)...), true
		}
	}
	return path.Join(append([]string{"/skydns/"}, l...)...), false
}

// Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local.,
// the resulting key will be /skydns/local/skydns/staging/service .
func Path(s string) string {
	l := dns.SplitDomainName(s)
	for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	return path.Join(append([]string{"/skydns/"}, l...)...)
}

// Domain is the opposite of Path.
func Domain(s string) string {
	l := strings.Split(s, "/")
	// start with 1, to strip /skydns
	for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
}

// Split255 splits a string into 255 byte chunks.
func split255(s string) []string {
	if len(s) < 255 {
		return []string{s}
	}
	sx := []string{}
	p, i := 0, 255
	for {
		if i <= len(s) {
			sx = append(sx, s[p:i])
		} else {
			sx = append(sx, s[p:])
			break

		}
		p, i = p+255, i+255
	}

	return sx
}
<commit_msg>Add Mail boolean to MX records<commit_after>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package msg

import (
	"net"
	"path"
	"strings"

	"github.com/miekg/dns"
)

// This *is* the rdata from a SRV record, but with a twist.
// Host (Target in SRV) must be a domain name, but if it looks like an IP
// address (4/6), we will treat it like an IP address.
type Service struct {
	Host     string `json:"host,omitempty"`
	Port     int    `json:"port,omitempty"`
	Priority int    `json:"priority,omitempty"`
	Weight   int    `json:"weight,omitempty"`
	Text     string `json:"text,omitempty"`
	Mail     bool   `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
	Ttl      uint32 `json:"ttl,omitempty"`

	// When a SRV record with a "Host: IP-address" is added, we synthesize
	// a srv.Target domain name.  Normally we convert the full Key where
	// the record lives to a DNS name and use this as the srv.Target.  When
	// TargetStrip > 0 we strip the left most TargetStrip labels from the
	// DNS name.
	TargetStrip int `json:"targetstrip",omitempty"`

	// Etcd key where we found this service and ignored from json un-/marshalling
	Key string `json:"-"`
}

// NewSRV returns a new SRV record based on the Service.
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
	host := dns.Fqdn(s.Host)

	offset, end := 0, false
	for i := 0; i < s.TargetStrip; i++ {
		offset, end = dns.NextLabel(host, offset)
	}
	if end {
		// We overshot the name, use the orignal one.
		offset = 0
	}
	host = host[offset:]

	return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.Ttl},
		Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: host}
}

// NewMX returns a new MX record based on the Service.
func (s *Service) NewMX(name string, weight uint16) *dns.MX {
	host := dns.Fqdn(s.Host)

	offset, end := 0, false
	for i := 0; i < s.TargetStrip; i++ {
		offset, end = dns.NextLabel(host, offset)
	}
	if end {
		// We overshot the name, use the orignal one.
		offset = 0
	}
	host = host[offset:]

	return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.Ttl},
		Preference: uint16(s.Priority), Mx: host}
}

// NewA returns a new A record based on the Service.
func (s *Service) NewA(name string, ip net.IP) *dns.A {
	return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.Ttl}, A: ip}
}

// NewAAAA returns a new AAAA record based on the Service.
func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
	return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.Ttl}, AAAA: ip}
}

// NewCNAME returns a new CNAME record based on the Service.
func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
	return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.Ttl}, Target: target}
}

// NewNS returns a new NS record based on the Service.
func (s *Service) NewNS(name string, target string) *dns.NS {
	return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.Ttl}, Ns: target}
}

// NewTXT returns a new TXT record based on the Service.
func (s *Service) NewTXT(name string) *dns.TXT {
	return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)}
}

// NewPTR returns a new PTR record based on the Service.
func (s *Service) NewPTR(name string, ttl uint32) *dns.PTR {
	return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}, Ptr: dns.Fqdn(s.Host)}
}

// As Path, but
// if a name contains wildcards (*), the name will be chopped of before the (first) wildcard, and
// we do a highler evel search and later find the matching names.
// So service.*.skydns.local, will look for all services under skydns.local and will later check
// for names that match service.*.skydns.local.  If a wildcard is found the returned bool is true.
func PathWithWildcard(s string) (string, bool) {
	l := dns.SplitDomainName(s)
	for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	for i, k := range l {
		if k == "*" {
			return path.Join(append([]string{"/skydns/"}, l[:i]...)...), true
		}
	}
	return path.Join(append([]string{"/skydns/"}, l...)...), false
}

// Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local.,
// the resulting key will be /skydns/local/skydns/staging/service .
func Path(s string) string {
	l := dns.SplitDomainName(s)
	for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	return path.Join(append([]string{"/skydns/"}, l...)...)
}

// Domain is the opposite of Path.
func Domain(s string) string {
	l := strings.Split(s, "/")
	// start with 1, to strip /skydns
	for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 {
		l[i], l[j] = l[j], l[i]
	}
	return dns.Fqdn(strings.Join(l[1:len(l)-1], "."))
}

// Split255 splits a string into 255 byte chunks.
func split255(s string) []string {
	if len(s) < 255 {
		return []string{s}
	}
	sx := []string{}
	p, i := 0, 255
	for {
		if i <= len(s) {
			sx = append(sx, s[p:i])
		} else {
			sx = append(sx, s[p:])
			break

		}
		p, i = p+255, i+255
	}

	return sx
}
<|endoftext|>


<commit_before>package strategy

import (
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

// TODO: at some point, the strategy needs to plug in here
// to help decide how to sort tasks (on add) and how to select
// tasks (on getnext). For now, we are assuming a dumb/nice strategy.
type TaskList struct {
	tasks   []*Task
	taskmap map[u.Key]*Task
}

func NewTaskList() *TaskList {
	return &TaskList{
		taskmap: make(map[u.Key]*Task),
	}
}

type Task struct {
	Key           u.Key
	Target        peer.Peer
	theirPriority int
}

// Push currently adds a new task to the end of the list
// TODO: make this into a priority queue
func (tl *TaskList) Push(block u.Key, priority int, to peer.Peer) {
	if task, ok := tl.taskmap[to.Key()+block]; ok {
		// TODO: when priority queue is implemented,
		//       rearrange this Task
		task.theirPriority = priority
		return
	}
	task := &Task{
		Key:           block,
		Target:        to,
		theirPriority: priority,
	}
	tl.tasks = append(tl.tasks, task)
	tl.taskmap[to.Key()+block] = task
}

// Pop returns the next task to be performed by bitswap the task is then
// removed from the list
func (tl *TaskList) Pop() *Task {
	var out *Task
	for len(tl.tasks) > 0 {
		// TODO: instead of zero, use exponential distribution
		//       it will help reduce the chance of receiving
		//		 the same block from multiple peers
		out = tl.tasks[0]
		tl.tasks = tl.tasks[1:]
		delete(tl.taskmap, out.Target.Key()+out.Key)
		// Filter out blocks that have been cancelled
		if out.theirPriority >= 0 {
			break
		}
	}

	return out
}

// Cancel lazily cancels the sending of a block to a given peer
func (tl *TaskList) Cancel(k u.Key, p peer.Peer) {
	t, ok := tl.taskmap[p.Key()+k]
	if ok {
		t.theirPriority = -1
	}
}
<commit_msg>doc: comment<commit_after>package strategy

import (
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

// TODO: at some point, the strategy needs to plug in here
// to help decide how to sort tasks (on add) and how to select
// tasks (on getnext). For now, we are assuming a dumb/nice strategy.
type TaskList struct {
	tasks   []*Task
	taskmap map[u.Key]*Task
}

func NewTaskList() *TaskList {
	return &TaskList{
		taskmap: make(map[u.Key]*Task),
	}
}

type Task struct {
	Key           u.Key
	Target        peer.Peer
	theirPriority int
}

// Push currently adds a new task to the end of the list
// TODO: make this into a priority queue
func (tl *TaskList) Push(block u.Key, priority int, to peer.Peer) {
	if task, ok := tl.taskmap[to.Key()+block]; ok {
		// TODO: when priority queue is implemented,
		//       rearrange this Task
		task.theirPriority = priority
		return
	}
	task := &Task{
		Key:           block,
		Target:        to,
		theirPriority: priority,
	}
	tl.tasks = append(tl.tasks, task)
	tl.taskmap[to.Key()+block] = task
}

// Pop 'pops' the next task to be performed. Returns nil no task exists.
func (tl *TaskList) Pop() *Task {
	var out *Task
	for len(tl.tasks) > 0 {
		// TODO: instead of zero, use exponential distribution
		//       it will help reduce the chance of receiving
		//		 the same block from multiple peers
		out = tl.tasks[0]
		tl.tasks = tl.tasks[1:]
		delete(tl.taskmap, out.Target.Key()+out.Key)
		// Filter out blocks that have been cancelled
		if out.theirPriority >= 0 {
			break
		}
	}

	return out
}

// Cancel lazily cancels the sending of a block to a given peer
func (tl *TaskList) Cancel(k u.Key, p peer.Peer) {
	t, ok := tl.taskmap[p.Key()+k]
	if ok {
		t.theirPriority = -1
	}
}
<|endoftext|>