<commit_before>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/maniksurtani/quotaservice/master/LICENSE

// Package redis implements token buckets backed by Redis, inspired by the algorithms used in Guava's
// RateLimiter library - https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/RateLimiter.java
package redis

import (
	"fmt"
	"strconv"
	"time"

	"gopkg.in/redis.v5"

	"github.com/maniksurtani/quotaservice"
	"github.com/maniksurtani/quotaservice/logging"

	"sync"

	pbconfig "github.com/maniksurtani/quotaservice/protos/config"
)

// Suffixes for Redis keys
const (
	tokensNextAvblNanosSuffix = "TNA"
	accumulatedTokensSuffix   = "AT"
)

// redisBucket is threadsafe since it delegates concurrency to the Redis instance.
type redisBucket struct {
	dynamic                    bool
	cfg                        *pbconfig.BucketConfig
	factory                    *bucketFactory
	nanosBetweenTokens         string
	maxTokensToAccumulate      string
	maxIdleTimeMillis          string
	maxDebtNanos               string
	redisKeys                  []string // {tokensNextAvailableRedisKey, accumulatedTokensRedisKey}
	quotaservice.DefaultBucket          // Extension for default methods on interface
}

type bucketFactory struct {
	cfg               *pbconfig.ServiceConfig
	client            *redis.Client
	redisOpts         *redis.Options
	scriptSHA         string
	connectionRetries int
	mu                sync.Mutex
}

func NewBucketFactory(redisOpts *redis.Options, connectionRetries int) quotaservice.BucketFactory {
	if connectionRetries < 1 {
		connectionRetries = 1
	}

	return &bucketFactory{
		redisOpts:         redisOpts,
		connectionRetries: connectionRetries}
}

func (bf *bucketFactory) Init(cfg *pbconfig.ServiceConfig) {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	bf.cfg = cfg

	if bf.client == nil {
		bf.connectToRedisLocked()
	}

	// Clean out all buckets, start afresh.
	bf.client.FlushDb()
}

func (bf *bucketFactory) connectToRedisLocked() {
	// Set up connection to Redis
	bf.client = redis.NewClient(bf.redisOpts)

	time, err := bf.client.Time().Result()
	if err != nil {
		logging.Printf("Cannot connect to Redis. TIME returned %v", err)
	} else {
		logging.Printf("Connection established. Time on Redis server: %v", time)
	}

	bf.scriptSHA = loadScript(bf.client)
}

func (bf *bucketFactory) reconnectToRedis(oldClient *redis.Client) {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	if oldClient == bf.client {
		bf.connectToRedisLocked()
	}
}

func (bf *bucketFactory) Client() interface{} {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	return bf.client
}

func (bf *bucketFactory) NewBucket(namespace, bucketName string, cfg *pbconfig.BucketConfig, dyn bool) quotaservice.Bucket {
	idle := "0"
	if cfg.MaxIdleMillis > 0 {
		idle = strconv.FormatInt(int64(cfg.MaxIdleMillis), 10)
	}

	rb := &redisBucket{
		dyn,
		cfg,
		bf,
		strconv.FormatInt(1e9/cfg.FillRate, 10),
		strconv.FormatInt(cfg.Size, 10),
		idle,
		strconv.FormatInt(cfg.MaxDebtMillis*1e6, 10), // Convert millis to nanos
		[]string{toRedisKey(namespace, bucketName, tokensNextAvblNanosSuffix),
			toRedisKey(namespace, bucketName, accumulatedTokensSuffix)},
		*new(quotaservice.DefaultBucket)}

	return rb
}

func toRedisKey(namespace, bucketName, suffix string) string {
	return namespace + ":" + bucketName + ":" + suffix
}

func (b *redisBucket) Take(requested int64, maxWaitTime time.Duration) (time.Duration, bool) {
	currentTimeNanos := strconv.FormatInt(time.Now().UnixNano(), 10)
	args := []interface{}{currentTimeNanos, b.nanosBetweenTokens, b.maxTokensToAccumulate,
		strconv.FormatInt(requested, 10), strconv.FormatInt(maxWaitTime.Nanoseconds(), 10),
		b.maxIdleTimeMillis, b.maxDebtNanos}

	keepTrying := true
	var waitTime time.Duration
	for attempt := 0; keepTrying && attempt < b.factory.connectionRetries; attempt++ {
		client := b.factory.Client().(*redis.Client)
		res := client.EvalSha(b.factory.scriptSHA, b.redisKeys, args...)
		switch waitTimeNanos := res.Val().(type) {
		case int64:
			waitTime = time.Nanosecond * time.Duration(waitTimeNanos)
			keepTrying = false
		default:
			// Always close connections on errors to prevent results leaking.
			if err := b.factory.client.Close(); err != nil {
				logging.Printf("Received error on redis client close: %+v", err)
			}

			if res.Err() != nil && res.Err().Error() == "redis: client is closed" {
				b.factory.reconnectToRedis(client)
			} else {
				logging.Printf("Unknown response '%v' of type %T. Full result %+v",
					waitTimeNanos, waitTimeNanos, res)
				b.factory.reconnectToRedis(client)
			}
		}
	}

	if keepTrying {
		panic(fmt.Sprintf("Couldn't reconnect to Redis, even after %v attempts",
			b.factory.connectionRetries))
	}

	if waitTime < 0 {
		// Timed out
		return 0, false
	}

	return waitTime, true
}

func (b *redisBucket) Config() *pbconfig.BucketConfig {
	return b.cfg
}

func (b *redisBucket) Dynamic() bool {
	return b.dynamic
}

func checkScriptExists(c *redis.Client, sha string) bool {
	r := c.ScriptExists(sha)
	return r.Val()[0]
}

// loadScript loads the LUA script into Redis. The LUA script contains the token bucket algorithm
// which is executed atomically in Redis. Once the script is loaded, it is invoked using its SHA.
func loadScript(c *redis.Client) (sha string) {
	lua := `
	local tokensNextAvailableNanos = tonumber(redis.call("GET", KEYS[1]))
	if not tokensNextAvailableNanos then
		tokensNextAvailableNanos = 0
	end

	local maxTokensToAccumulate = tonumber(ARGV[3])

	local accumulatedTokens = redis.call("GET", KEYS[2])
	if not accumulatedTokens then
		accumulatedTokens = maxTokensToAccumulate
	end

	local currentTimeNanos = tonumber(ARGV[1])
	local nanosBetweenTokens = tonumber(ARGV[2])
	local requested = tonumber(ARGV[4])
	local maxWaitTime = tonumber(ARGV[5])
	local lifespan = tonumber(ARGV[6])
	local maxDebtNanos = tonumber(ARGV[7])
	local freshTokens = 0

	if currentTimeNanos > tokensNextAvailableNanos then
		freshTokens = math.floor((currentTimeNanos - tokensNextAvailableNanos) / nanosBetweenTokens)
		accumulatedTokens = math.min(maxTokensToAccumulate, accumulatedTokens + freshTokens)
		tokensNextAvailableNanos = currentTimeNanos
	end

	local waitTime = tokensNextAvailableNanos - currentTimeNanos
	local accumulatedTokensUsed = math.min(accumulatedTokens, requested)
	local tokensToWaitFor = requested - accumulatedTokensUsed
	local futureWaitNanos = tokensToWaitFor * nanosBetweenTokens

	tokensNextAvailableNanos = tokensNextAvailableNanos + futureWaitNanos
	accumulatedTokens = accumulatedTokens - accumulatedTokensUsed

	if (tokensNextAvailableNanos - currentTimeNanos > maxDebtNanos) or (waitTime > 0 and waitTime > maxWaitTime) then
    	waitTime = -1
	else
		if lifespan > 0 then
			redis.call("SET", KEYS[1], tokensNextAvailableNanos, "PX", lifespan)
			redis.call("SET", KEYS[2], math.floor(accumulatedTokens), "PX", lifespan)
		else
			redis.call("SET", KEYS[1], tokensNextAvailableNanos)
			redis.call("SET", KEYS[2], math.floor(accumulatedTokens))
		end
	end

	return waitTime
	`
	s := c.ScriptLoad(lua)
	sha = s.Val()
	logging.Printf("Loaded LUA script into Redis; script SHA %v", sha)
	return
}
<commit_msg>redis: quiet some error logging + simplify (#62)<commit_after>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/maniksurtani/quotaservice/master/LICENSE

// Package redis implements token buckets backed by Redis, inspired by the algorithms used in Guava's
// RateLimiter library - https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/RateLimiter.java
package redis

import (
	"fmt"
	"strconv"
	"time"

	"gopkg.in/redis.v5"

	"github.com/maniksurtani/quotaservice"
	"github.com/maniksurtani/quotaservice/logging"

	"sync"

	pbconfig "github.com/maniksurtani/quotaservice/protos/config"
)

// Suffixes for Redis keys
const (
	tokensNextAvblNanosSuffix = "TNA"
	accumulatedTokensSuffix   = "AT"
)

// redisBucket is threadsafe since it delegates concurrency to the Redis instance.
type redisBucket struct {
	dynamic                    bool
	cfg                        *pbconfig.BucketConfig
	factory                    *bucketFactory
	nanosBetweenTokens         string
	maxTokensToAccumulate      string
	maxIdleTimeMillis          string
	maxDebtNanos               string
	redisKeys                  []string // {tokensNextAvailableRedisKey, accumulatedTokensRedisKey}
	quotaservice.DefaultBucket          // Extension for default methods on interface
}

type bucketFactory struct {
	cfg               *pbconfig.ServiceConfig
	client            *redis.Client
	redisOpts         *redis.Options
	scriptSHA         string
	connectionRetries int
	mu                sync.Mutex
}

func NewBucketFactory(redisOpts *redis.Options, connectionRetries int) quotaservice.BucketFactory {
	if connectionRetries < 1 {
		connectionRetries = 1
	}

	return &bucketFactory{
		redisOpts:         redisOpts,
		connectionRetries: connectionRetries}
}

func (bf *bucketFactory) Init(cfg *pbconfig.ServiceConfig) {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	bf.cfg = cfg

	if bf.client == nil {
		bf.connectToRedisLocked()
	}

	// Clean out all buckets, start afresh.
	bf.client.FlushDb()
}

func (bf *bucketFactory) connectToRedisLocked() {
	// Set up connection to Redis
	bf.client = redis.NewClient(bf.redisOpts)

	time, err := bf.client.Time().Result()
	if err != nil {
		logging.Printf("Cannot connect to Redis. TIME returned %v", err)
	} else {
		logging.Printf("Connection established. Time on Redis server: %v", time)
	}

	bf.scriptSHA = loadScript(bf.client)
}

func (bf *bucketFactory) reconnectToRedis(oldClient *redis.Client) {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	// Always close connections on errors to prevent results leaking.
	if err := bf.client.Close(); unknownCloseError(err) {
		logging.Printf("Received error on redis client close: %+v", err)
	}

	if oldClient == bf.client {
		bf.connectToRedisLocked()
	}
}

func (bf *bucketFactory) Client() interface{} {
	bf.mu.Lock()
	defer bf.mu.Unlock()

	return bf.client
}

func (bf *bucketFactory) NewBucket(namespace, bucketName string, cfg *pbconfig.BucketConfig, dyn bool) quotaservice.Bucket {
	idle := "0"
	if cfg.MaxIdleMillis > 0 {
		idle = strconv.FormatInt(int64(cfg.MaxIdleMillis), 10)
	}

	rb := &redisBucket{
		dyn,
		cfg,
		bf,
		strconv.FormatInt(1e9/cfg.FillRate, 10),
		strconv.FormatInt(cfg.Size, 10),
		idle,
		strconv.FormatInt(cfg.MaxDebtMillis*1e6, 10), // Convert millis to nanos
		[]string{toRedisKey(namespace, bucketName, tokensNextAvblNanosSuffix),
			toRedisKey(namespace, bucketName, accumulatedTokensSuffix)},
		*new(quotaservice.DefaultBucket)}

	return rb
}

func toRedisKey(namespace, bucketName, suffix string) string {
	return namespace + ":" + bucketName + ":" + suffix
}

func unknownCloseError(err error) bool {
	return err != nil && err.Error() != "redis: client is closed"
}

func (b *redisBucket) Take(requested int64, maxWaitTime time.Duration) (time.Duration, bool) {
	currentTimeNanos := strconv.FormatInt(time.Now().UnixNano(), 10)
	args := []interface{}{currentTimeNanos, b.nanosBetweenTokens, b.maxTokensToAccumulate,
		strconv.FormatInt(requested, 10), strconv.FormatInt(maxWaitTime.Nanoseconds(), 10),
		b.maxIdleTimeMillis, b.maxDebtNanos}

	keepTrying := true
	var waitTime time.Duration
	for attempt := 0; keepTrying && attempt < b.factory.connectionRetries; attempt++ {
		client := b.factory.Client().(*redis.Client)
		res := client.EvalSha(b.factory.scriptSHA, b.redisKeys, args...)
		switch waitTimeNanos := res.Val().(type) {
		case int64:
			waitTime = time.Nanosecond * time.Duration(waitTimeNanos)
			keepTrying = false
		default:
			if unknownCloseError(res.Err()) {
				logging.Printf("Unknown response '%v' of type %T. Full result %+v",
					waitTimeNanos, waitTimeNanos, res)
			}

			b.factory.reconnectToRedis(client)
		}
	}

	if keepTrying {
		panic(fmt.Sprintf("Couldn't reconnect to Redis, even after %v attempts",
			b.factory.connectionRetries))
	}

	if waitTime < 0 {
		// Timed out
		return 0, false
	}

	return waitTime, true
}

func (b *redisBucket) Config() *pbconfig.BucketConfig {
	return b.cfg
}

func (b *redisBucket) Dynamic() bool {
	return b.dynamic
}

func checkScriptExists(c *redis.Client, sha string) bool {
	r := c.ScriptExists(sha)
	return r.Val()[0]
}

// loadScript loads the LUA script into Redis. The LUA script contains the token bucket algorithm
// which is executed atomically in Redis. Once the script is loaded, it is invoked using its SHA.
func loadScript(c *redis.Client) (sha string) {
	lua := `
	local tokensNextAvailableNanos = tonumber(redis.call("GET", KEYS[1]))
	if not tokensNextAvailableNanos then
		tokensNextAvailableNanos = 0
	end

	local maxTokensToAccumulate = tonumber(ARGV[3])

	local accumulatedTokens = redis.call("GET", KEYS[2])
	if not accumulatedTokens then
		accumulatedTokens = maxTokensToAccumulate
	end

	local currentTimeNanos = tonumber(ARGV[1])
	local nanosBetweenTokens = tonumber(ARGV[2])
	local requested = tonumber(ARGV[4])
	local maxWaitTime = tonumber(ARGV[5])
	local lifespan = tonumber(ARGV[6])
	local maxDebtNanos = tonumber(ARGV[7])
	local freshTokens = 0

	if currentTimeNanos > tokensNextAvailableNanos then
		freshTokens = math.floor((currentTimeNanos - tokensNextAvailableNanos) / nanosBetweenTokens)
		accumulatedTokens = math.min(maxTokensToAccumulate, accumulatedTokens + freshTokens)
		tokensNextAvailableNanos = currentTimeNanos
	end

	local waitTime = tokensNextAvailableNanos - currentTimeNanos
	local accumulatedTokensUsed = math.min(accumulatedTokens, requested)
	local tokensToWaitFor = requested - accumulatedTokensUsed
	local futureWaitNanos = tokensToWaitFor * nanosBetweenTokens

	tokensNextAvailableNanos = tokensNextAvailableNanos + futureWaitNanos
	accumulatedTokens = accumulatedTokens - accumulatedTokensUsed

	if (tokensNextAvailableNanos - currentTimeNanos > maxDebtNanos) or (waitTime > 0 and waitTime > maxWaitTime) then
    	waitTime = -1
	else
		if lifespan > 0 then
			redis.call("SET", KEYS[1], tokensNextAvailableNanos, "PX", lifespan)
			redis.call("SET", KEYS[2], math.floor(accumulatedTokens), "PX", lifespan)
		else
			redis.call("SET", KEYS[1], tokensNextAvailableNanos)
			redis.call("SET", KEYS[2], math.floor(accumulatedTokens))
		end
	end

	return waitTime
	`
	s := c.ScriptLoad(lua)
	sha = s.Val()
	logging.Printf("Loaded LUA script into Redis; script SHA %v", sha)
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://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 check

import (
	"bytes"
	"fmt"
	"math/big"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/google/wuffs/lang/builtin"
	"github.com/google/wuffs/lang/parse"
	"github.com/google/wuffs/lang/render"

	a "github.com/google/wuffs/lang/ast"
	t "github.com/google/wuffs/lang/token"
)

func compareToWuffsfmt(tm *t.Map, tokens []t.Token, comments []string, src string) error {
	buf := &bytes.Buffer{}
	if err := render.Render(buf, tm, tokens, comments); err != nil {
		return err
	}
	got := strings.Split(buf.String(), "\n")
	want := strings.Split(src, "\n")
	for line := 1; len(got) != 0 || len(want) != 0; line++ {
		if len(got) == 0 {
			w := strings.TrimSpace(want[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, "", w)
		}
		if len(want) == 0 {
			g := strings.TrimSpace(got[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, "")
		}
		if g, w := strings.TrimSpace(got[0]), strings.TrimSpace(want[0]); g != w {
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, w)
		}
		got = got[1:]
		want = want[1:]
	}
	return nil
}

func TestCheck(tt *testing.T) {
	const filename = "test.wuffs"
	src := strings.TrimSpace(`
		packageid "test"

		pri struct foo(
			i base.i32,
		)

		pri func foo.bar()() {
			var x base.u8
			var y base.i32 = +2
			var z base.u64[..123]
			var a array[4] base.u8
			var b base.bool

			x = 0
			x = 1 + (x * 0)
			y = -y - 1
			y = this.i
			b = not true

			y = x as base.i32

			var p base.i32
			var q base.i32[0..8]

			assert true

			while:label p == q,
				pre true,
				inv true,
				post p != q,
			{
				// Redundant, but shows the labeled jump syntax.
				continue:label
			}
		}
	`) + "\n"

	tm := &t.Map{}

	tokens, comments, err := t.Tokenize(tm, filename, []byte(src))
	if err != nil {
		tt.Fatalf("Tokenize: %v", err)
	}

	file, err := parse.Parse(tm, filename, tokens, nil)
	if err != nil {
		tt.Fatalf("Parse: %v", err)
	}

	if err := compareToWuffsfmt(tm, tokens, comments, src); err != nil {
		tt.Fatalf("compareToWuffsfmt: %v", err)
	}

	c, err := Check(tm, []*a.File{file}, nil)
	if err != nil {
		tt.Fatalf("Check: %v", err)
	}

	if len(c.funcs) != 1 {
		tt.Fatalf("Funcs: got %d elements, want 1", len(c.funcs))
	}
	fooBar := (*a.Func)(nil)
	fooBarLocalVars := typeMap(nil)
	for _, f := range c.funcs {
		fooBar = f
		break
	}
	for _, v := range c.localVars {
		fooBarLocalVars = v
		break
	}

	if got, want := fooBar.QQID().Str(tm), "foo.bar"; got != want {
		tt.Fatalf("Funcs[0] name: got %q, want %q", got, want)
	}

	got := [][2]string(nil)
	for id, typ := range fooBarLocalVars {
		got = append(got, [2]string{
			id.Str(tm),
			typ.Str(tm),
		})
	}
	sort.Slice(got, func(i, j int) bool {
		return got[i][0] < got[j][0]
	})

	want := [][2]string{
		{"a", "array[4] base.u8"},
		{"b", "base.bool"},
		{"in", "in"},
		{"out", "out"},
		{"p", "base.i32"},
		{"q", "base.i32[0..8]"},
		{"this", "ptr foo"},
		{"x", "base.u8"},
		{"y", "base.i32"},
		{"z", "base.u64[..123]"},
	}
	if !reflect.DeepEqual(got, want) {
		tt.Fatalf("\ngot  %v\nwant %v", got, want)
	}
}

func TestConstValues(tt *testing.T) {
	const filename = "test.wuffs"
	testCases := map[string]int64{
		"var i base.i32 = 42": 42,

		"var i base.i32 = +7": +7,
		"var i base.i32 = -7": -7,

		"var b base.bool = false":         0,
		"var b base.bool = not false":     1,
		"var b base.bool = not not false": 0,

		"var i base.i32 = 10  + 3": 13,
		"var i base.i32 = 10  - 3": 7,
		"var i base.i32 = 10  * 3": 30,
		"var i base.i32 = 10  / 3": 3,
		"var i base.i32 = 10 << 3": 80,
		"var i base.i32 = 10 >> 3": 1,
		"var i base.i32 = 10  & 3": 2,
		"var i base.i32 = 10  | 3": 11,
		"var i base.i32 = 10  ^ 3": 9,

		"var b base.bool = 10 != 3": 1,
		"var b base.bool = 10  < 3": 0,
		"var b base.bool = 10 <= 3": 0,
		"var b base.bool = 10 == 3": 0,
		"var b base.bool = 10 >= 3": 1,
		"var b base.bool = 10  > 3": 1,

		"var b base.bool = false and true": 0,
		"var b base.bool = false  or true": 1,
	}

	tm := &t.Map{}
	for s, wantInt64 := range testCases {
		src := "packageid \"test\"\npri func foo()() {\n\t" + s + "\n}\n"

		tokens, _, err := t.Tokenize(tm, filename, []byte(src))
		if err != nil {
			tt.Errorf("%q: Tokenize: %v", s, err)
			continue
		}

		file, err := parse.Parse(tm, filename, tokens, nil)
		if err != nil {
			tt.Errorf("%q: Parse: %v", s, err)
			continue
		}

		c, err := Check(tm, []*a.File{file}, nil)
		if err != nil {
			tt.Errorf("%q: Check: %v", s, err)
			continue
		}

		if len(c.funcs) != 1 {
			tt.Errorf("%q: Funcs: got %d elements, want 1", s, len(c.funcs))
			continue
		}
		foo := (*a.Func)(nil)
		for _, f := range c.funcs {
			foo = f
			break
		}
		body := foo.Body()
		if len(body) != 1 {
			tt.Errorf("%q: Body: got %d elements, want 1", s, len(body))
			continue
		}
		if body[0].Kind() != a.KVar {
			tt.Errorf("%q: Body[0]: got %s, want %s", s, body[0].Kind(), a.KVar)
			continue
		}

		got := body[0].Var().Value().ConstValue()
		want := big.NewInt(wantInt64)
		if got == nil || want == nil || got.Cmp(want) != 0 {
			tt.Errorf("%q: got %v, want %v", s, got, want)
			continue
		}
	}
}

func TestBitMask(tt *testing.T) {
	testCases := [][2]uint64{
		{0, 0},
		{1, 1},
		{2, 3},
		{3, 3},
		{4, 7},
		{5, 7},
		{50, 63},
		{500, 511},
		{5000, 8191},
		{50000, 65535},
		{500000, 524287},
		{1<<7 - 2, 1<<7 - 1},
		{1<<7 - 1, 1<<7 - 1},
		{1<<7 + 0, 1<<8 - 1},
		{1<<7 + 1, 1<<8 - 1},
		{1<<8 - 2, 1<<8 - 1},
		{1<<8 - 1, 1<<8 - 1},
		{1<<8 + 0, 1<<9 - 1},
		{1<<8 + 1, 1<<9 - 1},
		{1<<32 - 2, 1<<32 - 1},
		{1<<32 - 1, 1<<32 - 1},
		{1<<32 + 0, 1<<33 - 1},
		{1<<32 + 1, 1<<33 - 1},
		{1<<64 - 2, 1<<64 - 1},
		{1<<64 - 1, 1<<64 - 1},
	}

	for _, tc := range testCases {
		got := bitMask(big.NewInt(0).SetUint64(tc[0]).BitLen())
		want := big.NewInt(0).SetUint64(tc[1])
		if got.Cmp(want) != 0 {
			tt.Errorf("roundUpToPowerOf2Minus1(%v): got %v, want %v", tc[0], got, tc[1])
		}
	}
}

func TestBuiltInTypeMap(tt *testing.T) {
	if got, want := len(builtInTypeMap), len(builtin.Types); got != want {
		tt.Fatalf("lengths: got %d, want %d", got, want)
	}
	tm := t.Map{}
	for _, s := range builtin.Types {
		id := tm.ByName(s)
		if id == 0 {
			tt.Fatalf("ID(%q): got 0, want non-0", s)
		}
		if _, ok := builtInTypeMap[id]; !ok {
			tt.Fatalf("no builtInTypeMap entry for %q", s)
		}
	}
}
<commit_msg>Fix lang/check test for the implicit reset method<commit_after>// Copyright 2017 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://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 check

import (
	"bytes"
	"fmt"
	"math/big"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/google/wuffs/lang/builtin"
	"github.com/google/wuffs/lang/parse"
	"github.com/google/wuffs/lang/render"

	a "github.com/google/wuffs/lang/ast"
	t "github.com/google/wuffs/lang/token"
)

func compareToWuffsfmt(tm *t.Map, tokens []t.Token, comments []string, src string) error {
	buf := &bytes.Buffer{}
	if err := render.Render(buf, tm, tokens, comments); err != nil {
		return err
	}
	got := strings.Split(buf.String(), "\n")
	want := strings.Split(src, "\n")
	for line := 1; len(got) != 0 || len(want) != 0; line++ {
		if len(got) == 0 {
			w := strings.TrimSpace(want[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, "", w)
		}
		if len(want) == 0 {
			g := strings.TrimSpace(got[0])
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, "")
		}
		if g, w := strings.TrimSpace(got[0]), strings.TrimSpace(want[0]); g != w {
			return fmt.Errorf("difference at line %d:\n%s\n%s", line, g, w)
		}
		got = got[1:]
		want = want[1:]
	}
	return nil
}

func TestCheck(tt *testing.T) {
	const filename = "test.wuffs"
	src := strings.TrimSpace(`
		packageid "test"

		pri struct foo(
			i base.i32,
		)

		pri func foo.bar()() {
			var x base.u8
			var y base.i32 = +2
			var z base.u64[..123]
			var a array[4] base.u8
			var b base.bool

			x = 0
			x = 1 + (x * 0)
			y = -y - 1
			y = this.i
			b = not true

			y = x as base.i32

			var p base.i32
			var q base.i32[0..8]

			assert true

			while:label p == q,
				pre true,
				inv true,
				post p != q,
			{
				// Redundant, but shows the labeled jump syntax.
				continue:label
			}
		}
	`) + "\n"

	tm := &t.Map{}

	tokens, comments, err := t.Tokenize(tm, filename, []byte(src))
	if err != nil {
		tt.Fatalf("Tokenize: %v", err)
	}

	file, err := parse.Parse(tm, filename, tokens, nil)
	if err != nil {
		tt.Fatalf("Parse: %v", err)
	}

	if err := compareToWuffsfmt(tm, tokens, comments, src); err != nil {
		tt.Fatalf("compareToWuffsfmt: %v", err)
	}

	c, err := Check(tm, []*a.File{file}, nil)
	if err != nil {
		tt.Fatalf("Check: %v", err)
	}

	// There are 2 funcs: the implicit foo.reset method, and the explicit
	// foo.bar method.
	if len(c.funcs) != 2 {
		tt.Fatalf("c.funcs: got %d elements, want 2", len(c.funcs))
	}
	qqid := t.QQID{0, tm.ByName("foo"), tm.ByName("bar")}
	fooBar := c.funcs[qqid]
	if fooBar == nil {
		tt.Fatalf("c.funcs: no entry for %q", qqid.Str(tm))
	}
	fooBarLocalVars := c.localVars[qqid]
	if fooBarLocalVars == nil {
		tt.Fatalf("c.localVars: no entry for %q", qqid.Str(tm))
	}

	if got, want := fooBar.QQID().Str(tm), "foo.bar"; got != want {
		tt.Fatalf("func name: got %q, want %q", got, want)
	}

	got := [][2]string(nil)
	for id, typ := range fooBarLocalVars {
		got = append(got, [2]string{
			id.Str(tm),
			typ.Str(tm),
		})
	}
	sort.Slice(got, func(i, j int) bool {
		return got[i][0] < got[j][0]
	})

	want := [][2]string{
		{"a", "array[4] base.u8"},
		{"b", "base.bool"},
		{"in", "in"},
		{"out", "out"},
		{"p", "base.i32"},
		{"q", "base.i32[0..8]"},
		{"this", "ptr foo"},
		{"x", "base.u8"},
		{"y", "base.i32"},
		{"z", "base.u64[..123]"},
	}
	if !reflect.DeepEqual(got, want) {
		tt.Fatalf("\ngot  %v\nwant %v", got, want)
	}
}

func TestConstValues(tt *testing.T) {
	const filename = "test.wuffs"
	testCases := map[string]int64{
		"var i base.i32 = 42": 42,

		"var i base.i32 = +7": +7,
		"var i base.i32 = -7": -7,

		"var b base.bool = false":         0,
		"var b base.bool = not false":     1,
		"var b base.bool = not not false": 0,

		"var i base.i32 = 10  + 3": 13,
		"var i base.i32 = 10  - 3": 7,
		"var i base.i32 = 10  * 3": 30,
		"var i base.i32 = 10  / 3": 3,
		"var i base.i32 = 10 << 3": 80,
		"var i base.i32 = 10 >> 3": 1,
		"var i base.i32 = 10  & 3": 2,
		"var i base.i32 = 10  | 3": 11,
		"var i base.i32 = 10  ^ 3": 9,

		"var b base.bool = 10 != 3": 1,
		"var b base.bool = 10  < 3": 0,
		"var b base.bool = 10 <= 3": 0,
		"var b base.bool = 10 == 3": 0,
		"var b base.bool = 10 >= 3": 1,
		"var b base.bool = 10  > 3": 1,

		"var b base.bool = false and true": 0,
		"var b base.bool = false  or true": 1,
	}

	tm := &t.Map{}
	for s, wantInt64 := range testCases {
		src := "packageid \"test\"\npri func foo()() {\n\t" + s + "\n}\n"

		tokens, _, err := t.Tokenize(tm, filename, []byte(src))
		if err != nil {
			tt.Errorf("%q: Tokenize: %v", s, err)
			continue
		}

		file, err := parse.Parse(tm, filename, tokens, nil)
		if err != nil {
			tt.Errorf("%q: Parse: %v", s, err)
			continue
		}

		c, err := Check(tm, []*a.File{file}, nil)
		if err != nil {
			tt.Errorf("%q: Check: %v", s, err)
			continue
		}

		if len(c.funcs) != 1 {
			tt.Errorf("%q: Funcs: got %d elements, want 1", s, len(c.funcs))
			continue
		}
		foo := (*a.Func)(nil)
		for _, f := range c.funcs {
			foo = f
			break
		}
		body := foo.Body()
		if len(body) != 1 {
			tt.Errorf("%q: Body: got %d elements, want 1", s, len(body))
			continue
		}
		if body[0].Kind() != a.KVar {
			tt.Errorf("%q: Body[0]: got %s, want %s", s, body[0].Kind(), a.KVar)
			continue
		}

		got := body[0].Var().Value().ConstValue()
		want := big.NewInt(wantInt64)
		if got == nil || want == nil || got.Cmp(want) != 0 {
			tt.Errorf("%q: got %v, want %v", s, got, want)
			continue
		}
	}
}

func TestBitMask(tt *testing.T) {
	testCases := [][2]uint64{
		{0, 0},
		{1, 1},
		{2, 3},
		{3, 3},
		{4, 7},
		{5, 7},
		{50, 63},
		{500, 511},
		{5000, 8191},
		{50000, 65535},
		{500000, 524287},
		{1<<7 - 2, 1<<7 - 1},
		{1<<7 - 1, 1<<7 - 1},
		{1<<7 + 0, 1<<8 - 1},
		{1<<7 + 1, 1<<8 - 1},
		{1<<8 - 2, 1<<8 - 1},
		{1<<8 - 1, 1<<8 - 1},
		{1<<8 + 0, 1<<9 - 1},
		{1<<8 + 1, 1<<9 - 1},
		{1<<32 - 2, 1<<32 - 1},
		{1<<32 - 1, 1<<32 - 1},
		{1<<32 + 0, 1<<33 - 1},
		{1<<32 + 1, 1<<33 - 1},
		{1<<64 - 2, 1<<64 - 1},
		{1<<64 - 1, 1<<64 - 1},
	}

	for _, tc := range testCases {
		got := bitMask(big.NewInt(0).SetUint64(tc[0]).BitLen())
		want := big.NewInt(0).SetUint64(tc[1])
		if got.Cmp(want) != 0 {
			tt.Errorf("roundUpToPowerOf2Minus1(%v): got %v, want %v", tc[0], got, tc[1])
		}
	}
}

func TestBuiltInTypeMap(tt *testing.T) {
	if got, want := len(builtInTypeMap), len(builtin.Types); got != want {
		tt.Fatalf("lengths: got %d, want %d", got, want)
	}
	tm := t.Map{}
	for _, s := range builtin.Types {
		id := tm.ByName(s)
		if id == 0 {
			tt.Fatalf("ID(%q): got 0, want non-0", s)
		}
		if _, ok := builtInTypeMap[id]; !ok {
			tt.Fatalf("no builtInTypeMap entry for %q", s)
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSDataSourceIAMPolicyDocument_basic(t *testing.T) {
	// This really ought to be able to be a unit test rather than an
	// acceptance test, but just instantiating the AWS provider requires
	// some AWS API calls, and so this needs valid AWS credentials to work.
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test", "json",
						testAccAWSIAMPolicyDocumentExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_source(t *testing.T) {
	// This really ought to be able to be a unit test rather than an
	// acceptance test, but just instantiating the AWS provider requires
	// some AWS API calls, and so this needs valid AWS credentials to work.
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentSourceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source", "json",
						testAccAWSIAMPolicyDocumentSourceExpectedJSON,
					),
				),
			},
			{
				Config: testAccAWSIAMPolicyDocumentSourceBlankConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source_blank", "json",
						testAccAWSIAMPolicyDocumentSourceBlankExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_sourceConflicting(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentSourceConflictingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source_conflicting", "json",
						testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_override(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentOverrideConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_override", "json",
						testAccAWSIAMPolicyDocumentOverrideExpectedJSON,
					),
				),
			},
		},
	})
}

func testAccCheckStateValue(id, name, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[id]
		if !ok {
			return fmt.Errorf("Not found: %s", id)
		}
		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		v := rs.Primary.Attributes[name]
		if v != value {
			return fmt.Errorf(
				"Value for %s is %s, not %s", name, v, value)
		}

		return nil
	}
}

var testAccAWSIAMPolicyDocumentConfig = `
data "aws_iam_policy_document" "test" {
    policy_id = "policy_id"
    statement {
    	sid = "1"
        actions = [
            "s3:ListAllMyBuckets",
            "s3:GetBucketLocation",
        ]
        resources = [
            "arn:aws:s3:::*",
        ]
    }

    statement {
        actions = [
            "s3:ListBucket",
        ]
        resources = [
            "arn:aws:s3:::foo",
        ]
        condition {
            test = "StringLike"
            variable = "s3:prefix"
            values = [
                "home/",
                "home/&{aws:username}/",
            ]
        }

        not_principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        actions = [
            "s3:*",
        ]
        resources = [
            "arn:aws:s3:::foo/home/&{aws:username}",
            "arn:aws:s3:::foo/home/&{aws:username}/*",
        ]
        principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        effect = "Deny"
        not_actions = ["s3:*"]
        not_resources = ["arn:aws:s3:::*"]
    }

    # Normalization of wildcard principals
    statement {
        effect = "Allow"
        actions = ["kinesis:*"]
        principals {
            type = "AWS"
            identifiers = ["*"]
        }
    }
    statement {
        effect = "Allow"
        actions = ["firehose:*"]
        principals {
            type = "*"
            identifiers = ["*"]
        }
    }

}
`

var testAccAWSIAMPolicyDocumentExpectedJSON = `{
  "Version": "2012-10-17",
  "Id": "policy_id",
  "Statement": [
    {
      "Sid": "1",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::foo",
      "NotPrincipal": {
        "AWS": "arn:blahblah:example"
      },
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "home/${aws:username}/",
            "home/"
          ]
        }
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::foo/home/${aws:username}/*",
        "arn:aws:s3:::foo/home/${aws:username}"
      ],
      "Principal": {
        "AWS": "arn:blahblah:example"
      }
    },
    {
      "Sid": "",
      "Effect": "Deny",
      "NotAction": "s3:*",
      "NotResource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "kinesis:*",
      "Principal": {
        "AWS": "*"
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "firehose:*",
      "Principal": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceConfig = `
data "aws_iam_policy_document" "test" {
    policy_id = "policy_id"
    statement {
        sid = "1"
        actions = [
            "s3:ListAllMyBuckets",
            "s3:GetBucketLocation",
        ]
        resources = [
            "arn:aws:s3:::*",
        ]
    }

    statement {
        actions = [
            "s3:ListBucket",
        ]
        resources = [
            "arn:aws:s3:::foo",
        ]
        condition {
            test = "StringLike"
            variable = "s3:prefix"
            values = [
                "home/",
                "home/&{aws:username}/",
            ]
        }

        not_principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        actions = [
            "s3:*",
        ]
        resources = [
            "arn:aws:s3:::foo/home/&{aws:username}",
            "arn:aws:s3:::foo/home/&{aws:username}/*",
        ]
        principals {
            type = "AWS"
            identifiers = [
				"arn:blahblah:example",
				"arn:blahblahblah:example",
			]
        }
    }

    statement {
        effect = "Deny"
        not_actions = ["s3:*"]
        not_resources = ["arn:aws:s3:::*"]
    }

    # Normalization of wildcard principals
    statement {
        effect = "Allow"
        actions = ["kinesis:*"]
        principals {
            type = "AWS"
            identifiers = ["*"]
        }
    }
    statement {
        effect = "Allow"
        actions = ["firehose:*"]
        principals {
            type = "*"
            identifiers = ["*"]
        }
    }

}

data "aws_iam_policy_document" "test_source" {
    source_json = "${data.aws_iam_policy_document.test.json}"

    statement {
        sid       = "SourceJSONTest1"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceExpectedJSON = `{
  "Version": "2012-10-17",
  "Id": "policy_id",
  "Statement": [
    {
      "Sid": "1",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::foo",
      "NotPrincipal": {
        "AWS": "arn:blahblah:example"
      },
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "home/${aws:username}/",
            "home/"
          ]
        }
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::foo/home/${aws:username}/*",
        "arn:aws:s3:::foo/home/${aws:username}"
      ],
      "Principal": {
        "AWS": [
          "arn:blahblahblah:example",
          "arn:blahblah:example"
        ]
      }
    },
    {
      "Sid": "",
      "Effect": "Deny",
      "NotAction": "s3:*",
      "NotResource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "kinesis:*",
      "Principal": {
        "AWS": "*"
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "firehose:*",
      "Principal": "*"
    },
    {
      "Sid": "SourceJSONTest1",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceBlankConfig = `
data "aws_iam_policy_document" "test_source_blank" {
    source_json = ""

    statement {
        sid       = "SourceJSONTest2"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceBlankExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SourceJSONTest2",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceConflictingConfig = `
data "aws_iam_policy_document" "test_source" {
    statement {
        sid       = "SourceJSONTestConflicting"
        actions   = ["iam:*"]
        resources = ["*"]
    }
}

data "aws_iam_policy_document" "test_source_conflicting" {
    source_json = "${data.aws_iam_policy_document.test_source.json}"

    statement {
        sid       = "SourceJSONTestConflicting"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SourceJSONTestConflicting",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentOverrideConfig = `
data "aws_iam_policy_document" "override" {
  statement {
    sid = "SidToOverwrite"

    actions   = ["s3:*"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "test_override" {
  override_json = "${data.aws_iam_policy_document.override.json}"

  statement {
    actions   = ["ec2:*"]
    resources = ["*"]
  }

  statement {
    sid = "SidToOverwrite"

    actions = ["s3:*"]

    resources = [
      "arn:aws:s3:::somebucket",
      "arn:aws:s3:::somebucket/*",
    ]
  }
}
`

var testAccAWSIAMPolicyDocumentOverrideExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ec2:*",
      "Resource": "*"
    },
    {
      "Sid": "SidToOverwrite",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}`
<commit_msg>ds/aws_iam_policy_document: Add acceptance tests without statement<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSDataSourceIAMPolicyDocument_basic(t *testing.T) {
	// This really ought to be able to be a unit test rather than an
	// acceptance test, but just instantiating the AWS provider requires
	// some AWS API calls, and so this needs valid AWS credentials to work.
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test", "json",
						testAccAWSIAMPolicyDocumentExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_source(t *testing.T) {
	// This really ought to be able to be a unit test rather than an
	// acceptance test, but just instantiating the AWS provider requires
	// some AWS API calls, and so this needs valid AWS credentials to work.
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentSourceConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source", "json",
						testAccAWSIAMPolicyDocumentSourceExpectedJSON,
					),
				),
			},
			{
				Config: testAccAWSIAMPolicyDocumentSourceBlankConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source_blank", "json",
						testAccAWSIAMPolicyDocumentSourceBlankExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_sourceConflicting(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentSourceConflictingConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_source_conflicting", "json",
						testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_override(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentOverrideConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.test_override", "json",
						testAccAWSIAMPolicyDocumentOverrideExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_noStatementMerge(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentNoStatementMergeConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.yak_politik", "json",
						testAccAWSIAMPolicyDocumentNoStatementMergeExpectedJSON,
					),
				),
			},
		},
	})
}

func TestAccAWSDataSourceIAMPolicyDocument_noStatementOverride(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSIAMPolicyDocumentNoStatementOverrideConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckStateValue("data.aws_iam_policy_document.yak_politik", "json",
						testAccAWSIAMPolicyDocumentNoStatementOverrideExpectedJSON,
					),
				),
			},
		},
	})
}

func testAccCheckStateValue(id, name, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[id]
		if !ok {
			return fmt.Errorf("Not found: %s", id)
		}
		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		v := rs.Primary.Attributes[name]
		if v != value {
			return fmt.Errorf(
				"Value for %s is %s, not %s", name, v, value)
		}

		return nil
	}
}

var testAccAWSIAMPolicyDocumentConfig = `
data "aws_iam_policy_document" "test" {
    policy_id = "policy_id"
    statement {
    	sid = "1"
        actions = [
            "s3:ListAllMyBuckets",
            "s3:GetBucketLocation",
        ]
        resources = [
            "arn:aws:s3:::*",
        ]
    }

    statement {
        actions = [
            "s3:ListBucket",
        ]
        resources = [
            "arn:aws:s3:::foo",
        ]
        condition {
            test = "StringLike"
            variable = "s3:prefix"
            values = [
                "home/",
                "home/&{aws:username}/",
            ]
        }

        not_principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        actions = [
            "s3:*",
        ]
        resources = [
            "arn:aws:s3:::foo/home/&{aws:username}",
            "arn:aws:s3:::foo/home/&{aws:username}/*",
        ]
        principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        effect = "Deny"
        not_actions = ["s3:*"]
        not_resources = ["arn:aws:s3:::*"]
    }

    # Normalization of wildcard principals
    statement {
        effect = "Allow"
        actions = ["kinesis:*"]
        principals {
            type = "AWS"
            identifiers = ["*"]
        }
    }
    statement {
        effect = "Allow"
        actions = ["firehose:*"]
        principals {
            type = "*"
            identifiers = ["*"]
        }
    }

}
`

var testAccAWSIAMPolicyDocumentExpectedJSON = `{
  "Version": "2012-10-17",
  "Id": "policy_id",
  "Statement": [
    {
      "Sid": "1",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::foo",
      "NotPrincipal": {
        "AWS": "arn:blahblah:example"
      },
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "home/${aws:username}/",
            "home/"
          ]
        }
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::foo/home/${aws:username}/*",
        "arn:aws:s3:::foo/home/${aws:username}"
      ],
      "Principal": {
        "AWS": "arn:blahblah:example"
      }
    },
    {
      "Sid": "",
      "Effect": "Deny",
      "NotAction": "s3:*",
      "NotResource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "kinesis:*",
      "Principal": {
        "AWS": "*"
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "firehose:*",
      "Principal": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceConfig = `
data "aws_iam_policy_document" "test" {
    policy_id = "policy_id"
    statement {
        sid = "1"
        actions = [
            "s3:ListAllMyBuckets",
            "s3:GetBucketLocation",
        ]
        resources = [
            "arn:aws:s3:::*",
        ]
    }

    statement {
        actions = [
            "s3:ListBucket",
        ]
        resources = [
            "arn:aws:s3:::foo",
        ]
        condition {
            test = "StringLike"
            variable = "s3:prefix"
            values = [
                "home/",
                "home/&{aws:username}/",
            ]
        }

        not_principals {
            type = "AWS"
            identifiers = ["arn:blahblah:example"]
        }
    }

    statement {
        actions = [
            "s3:*",
        ]
        resources = [
            "arn:aws:s3:::foo/home/&{aws:username}",
            "arn:aws:s3:::foo/home/&{aws:username}/*",
        ]
        principals {
            type = "AWS"
            identifiers = [
				"arn:blahblah:example",
				"arn:blahblahblah:example",
			]
        }
    }

    statement {
        effect = "Deny"
        not_actions = ["s3:*"]
        not_resources = ["arn:aws:s3:::*"]
    }

    # Normalization of wildcard principals
    statement {
        effect = "Allow"
        actions = ["kinesis:*"]
        principals {
            type = "AWS"
            identifiers = ["*"]
        }
    }
    statement {
        effect = "Allow"
        actions = ["firehose:*"]
        principals {
            type = "*"
            identifiers = ["*"]
        }
    }

}

data "aws_iam_policy_document" "test_source" {
    source_json = "${data.aws_iam_policy_document.test.json}"

    statement {
        sid       = "SourceJSONTest1"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceExpectedJSON = `{
  "Version": "2012-10-17",
  "Id": "policy_id",
  "Statement": [
    {
      "Sid": "1",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::foo",
      "NotPrincipal": {
        "AWS": "arn:blahblah:example"
      },
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "home/${aws:username}/",
            "home/"
          ]
        }
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::foo/home/${aws:username}/*",
        "arn:aws:s3:::foo/home/${aws:username}"
      ],
      "Principal": {
        "AWS": [
          "arn:blahblahblah:example",
          "arn:blahblah:example"
        ]
      }
    },
    {
      "Sid": "",
      "Effect": "Deny",
      "NotAction": "s3:*",
      "NotResource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "kinesis:*",
      "Principal": {
        "AWS": "*"
      }
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "firehose:*",
      "Principal": "*"
    },
    {
      "Sid": "SourceJSONTest1",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceBlankConfig = `
data "aws_iam_policy_document" "test_source_blank" {
    source_json = ""

    statement {
        sid       = "SourceJSONTest2"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceBlankExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SourceJSONTest2",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentSourceConflictingConfig = `
data "aws_iam_policy_document" "test_source" {
    statement {
        sid       = "SourceJSONTestConflicting"
        actions   = ["iam:*"]
        resources = ["*"]
    }
}

data "aws_iam_policy_document" "test_source_conflicting" {
    source_json = "${data.aws_iam_policy_document.test_source.json}"

    statement {
        sid       = "SourceJSONTestConflicting"
        actions   = ["*"]
        resources = ["*"]
    }
}
`

var testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SourceJSONTestConflicting",
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentOverrideConfig = `
data "aws_iam_policy_document" "override" {
  statement {
    sid = "SidToOverwrite"

    actions   = ["s3:*"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "test_override" {
  override_json = "${data.aws_iam_policy_document.override.json}"

  statement {
    actions   = ["ec2:*"]
    resources = ["*"]
  }

  statement {
    sid = "SidToOverwrite"

    actions = ["s3:*"]

    resources = [
      "arn:aws:s3:::somebucket",
      "arn:aws:s3:::somebucket/*",
    ]
  }
}
`

var testAccAWSIAMPolicyDocumentOverrideExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ec2:*",
      "Resource": "*"
    },
    {
      "Sid": "SidToOverwrite",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentNoStatementMergeConfig = `
data "aws_iam_policy_document" "source" {
  statement {
    sid = ""
    actions   = ["ec2:DescribeAccountAttributes"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "override" {
  statement {
    sid = "OverridePlaceholder"
    actions   = ["s3:GetObject"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "yak_politik" {
  source_json = "${data.aws_iam_policy_document.source.json}"
  override_json = "${data.aws_iam_policy_document.override.json}"
}
`

var testAccAWSIAMPolicyDocumentNoStatementMergeExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ec2:DescribeAccountAttributes",
      "Resource": "*"
    },
    {
      "Sid": "OverridePlaceholder",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "*"
    }
  ]
}`

var testAccAWSIAMPolicyDocumentNoStatementOverrideConfig = `
data "aws_iam_policy_document" "source" {
  statement {
    sid = "OverridePlaceholder"
    actions   = ["ec2:DescribeAccountAttributes"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "override" {
  statement {
    sid = "OverridePlaceholder"
    actions   = ["s3:GetObject"]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "yak_politik" {
  source_json = "${data.aws_iam_policy_document.source.json}"
  override_json = "${data.aws_iam_policy_document.override.json}"
}
`

var testAccAWSIAMPolicyDocumentNoStatementOverrideExpectedJSON = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OverridePlaceholder",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "*"
    }
  ]
}`
<|endoftext|>


<commit_before>/*
Copyright 2014 Workiva, LLC

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

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

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

/*
Package palm implements parallel architecture-friendly latch-free
modifications (PALM).  Details can be found here:

http://cs.unc.edu/~sewall/palm.pdf

The primary purpose of the tree is to efficiently batch operations
in such a way that locks are not required.  This is most beneficial
for in-memory indices.  Otherwise, the operations have typical B-tree
time complexities.

You primarily see the benefits of multithreading in availability and
bulk operations, below is a benchmark against the B-plus tree in this
package.

BenchmarkBulkAddToExisting-8	200	   8690207 ns/op
BenchmarkBulkAddToExisting-8    100   16778514 ns/op
*/
package palm

import "github.com/Workiva/go-datastructures/slice/skip"

// Keys is a typed list of Key interfaces.
type Keys []Key

// Key defines items that can be inserted into or searched for
// in the tree.
type Key interface {
	// Compare should return an int indicating how this key relates
	// to the provided key.  -1 will indicate less than, 0 will indicate
	// equality, and 1 will indicate greater than.  Duplicate keys
	// are allowed, but duplicate IDs are not.
	Compare(skip.Entry) int
}

// BTree is the interface returned from this package's constructor.
type BTree interface {
	// Insert will insert the provided keys into the tree.
	Insert(...Key)
	// Get will return a key matching the associated provided
	// key if it exists.
	Get(...Key) Keys
	// Len returns the number of items in the tree.
	Len() uint64
	// Dispose will clean up any resources used by this tree.  This
	// must be called to prevent a memory leak.
	Dispose()
}
<commit_msg>Added benchmarks.<commit_after>/*
Copyright 2014 Workiva, LLC

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

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

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

/*
Package palm implements parallel architecture-friendly latch-free
modifications (PALM).  Details can be found here:

http://cs.unc.edu/~sewall/palm.pdf

The primary purpose of the tree is to efficiently batch operations
in such a way that locks are not required.  This is most beneficial
for in-memory indices.  Otherwise, the operations have typical B-tree
time complexities.

You primarily see the benefits of multithreading in availability and
bulk operations.

Benchmarks:

BenchmarkReadAndWrites-8	   		  1000	   1543648 ns/op
BenchmarkBulkAdd-8	    			  1000	   1705673 ns/op
BenchmarkBulkAddToExisting-8	       100	  70056512 ns/op
BenchmarkGet-8	  					100000	     17128 ns/op
BenchmarkBulkGet-8	    			  3000	    507249 ns/op
*/
package palm

import "github.com/Workiva/go-datastructures/slice/skip"

// Keys is a typed list of Key interfaces.
type Keys []Key

// Key defines items that can be inserted into or searched for
// in the tree.
type Key interface {
	// Compare should return an int indicating how this key relates
	// to the provided key.  -1 will indicate less than, 0 will indicate
	// equality, and 1 will indicate greater than.  Duplicate keys
	// are allowed, but duplicate IDs are not.
	Compare(skip.Entry) int
}

// BTree is the interface returned from this package's constructor.
type BTree interface {
	// Insert will insert the provided keys into the tree.
	Insert(...Key)
	// Get will return a key matching the associated provided
	// key if it exists.
	Get(...Key) Keys
	// Len returns the number of items in the tree.
	Len() uint64
	// Dispose will clean up any resources used by this tree.  This
	// must be called to prevent a memory leak.
	Dispose()
}
<|endoftext|>


<commit_before>package config

var version = "1.0.2"
<commit_msg>v1.0.3 -- static binaries<commit_after>package config

var version = "1.0.3"
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"fmt"
)

var (
	Version                                 = "???"
	buildSha, buildDate, buildArch, buildOS string
)

type BuildInfo struct {
	Version, Sha, Date, Arch, OS string
}

func (b BuildInfo) String() string {
	var buff bytes.Buffer
	buff.WriteString(fmt.Sprintf("version=%s", b.Version))

	if b.Sha != "" {
		buff.WriteString(fmt.Sprintf(", commit=%s", b.Sha))
	}
	if b.Date != "" {
		buff.WriteString(fmt.Sprintf(", build-date=%s", b.Date))
	}
	if b.Arch != "" {
		buff.WriteString(fmt.Sprintf(", build-arch=%s", b.Arch))
	}
	if b.OS != "" {
		buff.WriteString(fmt.Sprintf(", build-os=%s", b.OS))
	}
	return buff.String()
}

var CurrentBuildInfo = BuildInfo{
	Version: Version,
	Sha:     buildSha,
	Date:    buildDate,
	Arch:    buildArch,
	OS:      buildOS,
}
<commit_msg>Bumping to version 0.0.7<commit_after>package config

import (
	"bytes"
	"fmt"
)

var (
	Version                                 = "0.0.7"
	buildSha, buildDate, buildArch, buildOS string
)

type BuildInfo struct {
	Version, Sha, Date, Arch, OS string
}

func (b BuildInfo) String() string {
	var buff bytes.Buffer
	buff.WriteString(fmt.Sprintf("version=%s", b.Version))

	if b.Sha != "" {
		buff.WriteString(fmt.Sprintf(", commit=%s", b.Sha))
	}
	if b.Date != "" {
		buff.WriteString(fmt.Sprintf(", build-date=%s", b.Date))
	}
	if b.Arch != "" {
		buff.WriteString(fmt.Sprintf(", build-arch=%s", b.Arch))
	}
	if b.OS != "" {
		buff.WriteString(fmt.Sprintf(", build-os=%s", b.OS))
	}
	return buff.String()
}

var CurrentBuildInfo = BuildInfo{
	Version: Version,
	Sha:     buildSha,
	Date:    buildDate,
	Arch:    buildArch,
	OS:      buildOS,
}
<|endoftext|>


<commit_before>package victor

import (
    "github.com/brettbuddin/victor/campfire"
    "strings"
    "strconv"
    "log"
    "os"
)

type Campfire struct {
    *Robot
    account string
    token   string
    rooms   []int
    client *campfire.Client
}

func NewCampfire(robot *Robot) *Campfire {
    campfire := &Campfire{Robot: robot}

    account := os.Getenv("VICTOR_CAMPFIRE_ACCOUNT")
    token   := os.Getenv("VICTOR_CAMPFIRE_TOKEN")
    rooms   := os.Getenv("VICTOR_CAMPFIRE_ROOMS")

    if account == "" {
        log.Panic("No account set.")
    }

    if token == "" {
        log.Panic("No token set.")
    }

    if rooms == "" {
        log.Panic("No rooms set.")
    }

    campfire.account = account
    campfire.token   = token

    roomIdStrings := strings.Split(rooms, ",")
    roomsArr      := make([]int, 0)

    for _, id := range roomIdStrings {
        j, _ := strconv.Atoi(id)
        roomsArr = append(roomsArr, j)
    }

    campfire.rooms = roomsArr

    return campfire
}

func (self *Campfire) Run() {
    log.Print("Starting up...")

    rooms  := self.rooms
    client := self.Client()

    channel := make(chan *campfire.Message)

    for i := range rooms {
        details, err := client.Room(rooms[i]).Show()

        if err != nil {
            continue
        }
        log.Print("Fetched room info.")

        for _, user := range details.Users {
            self.RememberUser(&User{Id: user.Id, Name: user.Name})
            log.Print("Remembering: " + user.Name)
        }

        room := client.Room(rooms[i])
        err   = room.Join()

        if err != nil {
            continue
        }
        log.Print("Joined room.")

        room.Stream(channel)
        log.Print("Listening...")
    }

    for {
        in   := <-channel

        if in.Type == "TextMessage" {
            msg := &TextMessage{
                Id: in.Id,
                Body: in.Body,
                CreatedAt: in.CreatedAt,

                Send: self.Send(in.RoomId),
                Reply: self.Reply(in.RoomId, in.UserId),
            }

            self.Receive(msg)
        }
    }
}

func (self *Campfire) Send(roomId int) func(string) {
    room := self.Client().Room(roomId)

    return func(text string) {
        room.Say(text)
    }
}

func (self *Campfire) Reply(roomId int, userId int) func(string) {
    room   := self.Client().Room(roomId)
    user   := self.UserForId(userId)
    prefix := ""

    if user != nil {
       prefix = user.Name + ": "
    }

    return func(text string) {
        room.Say(prefix + text)
    }
}

func (self *Campfire) Client() *campfire.Client {
    if self.client == nil {
        client := campfire.NewClient(
            self.account,
            self.token,
        )

        self.client = client
    }

    return self.client
}
<commit_msg>Helping myself debug some.<commit_after>package victor

import (
    "github.com/brettbuddin/victor/campfire"
    "strings"
    "strconv"
    "log"
    "os"
)

type Campfire struct {
    *Robot
    account string
    token   string
    rooms   []int
    client *campfire.Client
}

func NewCampfire(robot *Robot) *Campfire {
    campfire := &Campfire{Robot: robot}

    account := os.Getenv("VICTOR_CAMPFIRE_ACCOUNT")
    token   := os.Getenv("VICTOR_CAMPFIRE_TOKEN")
    rooms   := os.Getenv("VICTOR_CAMPFIRE_ROOMS")

    if account == "" {
        log.Panic("No account set.")
    }

    if token == "" {
        log.Panic("No token set.")
    }

    if rooms == "" {
        log.Panic("No rooms set.")
    }

    campfire.account = account
    campfire.token   = token

    roomIdStrings := strings.Split(rooms, ",")
    roomsArr      := make([]int, 0)

    for _, id := range roomIdStrings {
        j, _ := strconv.Atoi(id)
        roomsArr = append(roomsArr, j)
    }

    campfire.rooms = roomsArr

    return campfire
}

func (self *Campfire) Run() {
    log.Print("Starting up...")

    rooms  := self.rooms
    client := self.Client()

    channel := make(chan *campfire.Message)

    for i := range rooms {
        details, err := client.Room(rooms[i]).Show()

        if err != nil {
            log.Printf("Error fetching room info %i: %s", rooms[i], err)
            continue
        }
        log.Print("Fetched room info.")

        for _, user := range details.Users {
            self.RememberUser(&User{Id: user.Id, Name: user.Name})
            log.Print("Remembering: " + user.Name)
        }

        room := client.Room(rooms[i])
        err   = room.Join()

        if err != nil {
            log.Printf("Error joining room %i: %s", rooms[i], err)
            continue
        }
        log.Print("Joined room.")

        room.Stream(channel)
        log.Print("Listening...")
    }

    for {
        in   := <-channel

        if in.Type == "TextMessage" {
            msg := &TextMessage{
                Id: in.Id,
                Body: in.Body,
                CreatedAt: in.CreatedAt,

                Send: self.Send(in.RoomId),
                Reply: self.Reply(in.RoomId, in.UserId),
            }

            self.Receive(msg)
        }
    }
}

func (self *Campfire) Send(roomId int) func(string) {
    room := self.Client().Room(roomId)

    return func(text string) {
        room.Say(text)
    }
}

func (self *Campfire) Reply(roomId int, userId int) func(string) {
    room   := self.Client().Room(roomId)
    user   := self.UserForId(userId)
    prefix := ""

    if user != nil {
       prefix = user.Name + ": "
    }

    return func(text string) {
        room.Say(prefix + text)
    }
}

func (self *Campfire) Client() *campfire.Client {
    if self.client == nil {
        client := campfire.NewClient(
            self.account,
            self.token,
        )

        self.client = client
    }

    return self.client
}
<|endoftext|>


<commit_before>package postgres

import (
	"fmt"
	"time"

	"gitgud.io/softashell/comfy-translator/translator"
	lru "github.com/hashicorp/golang-lru"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

type translationError int

const (
	errorNone           translationError = iota // Everything is fine
	errorMinor                                  // Connection timed out or something like that
	errorBadTranslation                         // Returned really bad translation
)

type Cache struct {
	db       *gorm.DB
	lrustore map[string]*lru.TwoQueueCache
}

type Item struct {
	Translation string
	ErrorCode   translationError
	ErrorText   string
	Timestamp   int64
}

type Translation struct {
	Text string `gorm:"primarykey"`
	Bucket string `gorm:"primaryKey"`
	Translation string
	ErrorCode   translationError
	ErrorText   string
	Timestamp   time.Time
}

func NewCache(connStr string, cacheSize int, translators []string) (*Cache, error) {
	db, err := gorm.Open(postgres.Open(connStr), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	err = db.AutoMigrate(&Translation{})
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	store := make(map[string]*lru.TwoQueueCache)
	for _, t := range translators {
		s, err := lru.New2Q(5000)
		if err != nil {
			log.Fatalf("Can't start memory cache for %s", t)
		}

		store[t] = s
	}

	cache := &Cache{db: db, lrustore: store}

	return cache, nil
}

func (c *Cache) Close() error {
	return nil
}

func (c *Cache) Put(bucketName, text, translation string, cerr error) error {
	errorCode := errorNone
	errorText := ""

	if cerr != nil {
		errorText = cerr.Error()

		switch cerr.(type) {
		case translator.BadTranslationError:
			errorCode = errorBadTranslation
		default:
			errorCode = errorMinor
		}
	}

	pgItem := Translation{
		Text: 			text,
		Bucket: 		bucketName,
		Translation: 	translation,
		ErrorCode:   	errorCode,
		ErrorText:   	errorText,
		Timestamp:   	time.Now().UTC(),
	}

	result := c.db.Create(&pgItem)
	if result.Error != nil {
		log.Fatal(errors.Wrap(result.Error, "failed to execute insert"))
	}

	// Add to memory cache
	i := Item{
		Translation: translation,
		ErrorCode:   errorCode,
		ErrorText:   errorText,
		Timestamp:   time.Now().UTC().Unix(),
	}

	c.lrustore[bucketName].Add(text, i)

	return result.Error
}

func (c *Cache) Get(bucketName, text string) (string, bool, error) {
	var found bool
	var translation string
	var errorCode translationError
	var errorText string
	var timestamp int64

	item, ok := c.lrustore[bucketName].Get(text)
	if ok {
		i := item.(Item)

		translation = i.Translation
		errorCode = i.ErrorCode
		errorText = i.ErrorText
	} else {
		i := Translation{}
		result := c.db.Limit(1).Find(&i, Translation{Text: text,	Bucket: bucketName})
		if result.Error != nil {
			return translation, found, nil
		}

		translation = i.Translation
		errorCode = i.ErrorCode
		errorText = i.ErrorText
	}

	if translation != "" {
		found = true
	}

	if errorCode != errorNone {
		errorTime := time.Unix(timestamp, 0)

		if time.Since(errorTime) > getCacheExpiration(errorCode) {
			i := Translation{}
			result := c.db.Delete(&i, Translation{Text: text,	Bucket: bucketName})
			if result.Error != nil {
				log.Warn("unable to delete item: ", result.Error)
			}

			c.lrustore[bucketName].Remove(text)

			// Act as if nothing was found
			return "", false, nil
		}

		return "", found, fmt.Errorf("%s", errorText)
	}

	return translation, found, nil
}
<commit_msg>Update on conflict<commit_after>package postgres

import (
	"fmt"
	"time"

	"gitgud.io/softashell/comfy-translator/translator"
	lru "github.com/hashicorp/golang-lru"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

type translationError int

const (
	errorNone           translationError = iota // Everything is fine
	errorMinor                                  // Connection timed out or something like that
	errorBadTranslation                         // Returned really bad translation
)

type Cache struct {
	db       *gorm.DB
	lrustore map[string]*lru.TwoQueueCache
}

type Item struct {
	Translation string
	ErrorCode   translationError
	ErrorText   string
	Timestamp   int64
}

type Translation struct {
	Text string `gorm:"primarykey"`
	Bucket string `gorm:"primaryKey"`
	Translation string
	ErrorCode   translationError
	ErrorText   string
	Timestamp   time.Time
}

func NewCache(connStr string, cacheSize int, translators []string) (*Cache, error) {
	db, err := gorm.Open(postgres.Open(connStr), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	err = db.AutoMigrate(&Translation{})
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	store := make(map[string]*lru.TwoQueueCache)
	for _, t := range translators {
		s, err := lru.New2Q(5000)
		if err != nil {
			log.Fatalf("Can't start memory cache for %s", t)
		}

		store[t] = s
	}

	cache := &Cache{db: db, lrustore: store}

	return cache, nil
}

func (c *Cache) Close() error {
	return nil
}

func (c *Cache) Put(bucketName, text, translation string, cerr error) error {
	errorCode := errorNone
	errorText := ""

	if cerr != nil {
		errorText = cerr.Error()

		switch cerr.(type) {
		case translator.BadTranslationError:
			errorCode = errorBadTranslation
		default:
			errorCode = errorMinor
		}
	}

	pgItem := Translation{
		Text: 			text,
		Bucket: 		bucketName,
		Translation: 	translation,
		ErrorCode:   	errorCode,
		ErrorText:   	errorText,
		Timestamp:   	time.Now().UTC(),
	}

	result := c.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&pgItem)
	if result.Error != nil {
		log.Fatal(errors.Wrap(result.Error, "failed to execute insert"))
	}

	// Add to memory cache
	i := Item{
		Translation: translation,
		ErrorCode:   errorCode,
		ErrorText:   errorText,
		Timestamp:   time.Now().UTC().Unix(),
	}

	c.lrustore[bucketName].Add(text, i)

	return result.Error
}

func (c *Cache) Get(bucketName, text string) (string, bool, error) {
	var found bool
	var translation string
	var errorCode translationError
	var errorText string
	var timestamp int64

	item, ok := c.lrustore[bucketName].Get(text)
	if ok {
		i := item.(Item)

		translation = i.Translation
		errorCode = i.ErrorCode
		errorText = i.ErrorText
	} else {
		i := Translation{}
		result := c.db.Limit(1).Find(&i, Translation{Text: text,	Bucket: bucketName})
		if result.Error != nil {
			return translation, found, nil
		}

		translation = i.Translation
		errorCode = i.ErrorCode
		errorText = i.ErrorText
	}

	if translation != "" {
		found = true
	}

	if errorCode != errorNone {
		errorTime := time.Unix(timestamp, 0)

		if time.Since(errorTime) > getCacheExpiration(errorCode) {
			i := Translation{}
			result := c.db.Delete(&i, Translation{Text: text,	Bucket: bucketName})
			if result.Error != nil {
				log.Warn("unable to delete item: ", result.Error)
			}

			c.lrustore[bucketName].Remove(text)

			// Act as if nothing was found
			return "", false, nil
		}

		return "", found, fmt.Errorf("%s", errorText)
	}

	return translation, found, nil
}
<|endoftext|>


<commit_before>// prc_verifier
package openpgp

import (
	//"code.google.com/p/go.crypto/openpgp/armor"
	//"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	//"github.com/jmoiron/sqlx"
	"io"
	. "github.com/pruthvirajsinh/prlpks/errors"
	"strings"
	"time"
)

const (
	OTLVerified         int = 1
	OTLNotFound         int = 2
	OTLExpired          int = 3
	OTLNotVerified      int = 4
	OTLAlereadyVerified int = 5
	OTLNewOtlMade       int = 6
	ErrorSendingMail    int = 7
)

const ( //To create Email
	subject   string = "PKS:Please verify the request"
	otlPrefix string = "http://"
	otlMid    string = "/prc/verify?otl="
)

type Veri_data struct {
	//Database values
	//otl_hash,req_email,req_time,verify_time,verified,req_pubkey
	Req_email   string    `db:"req_email"`   // immutable
	Hash_pubkey string    `db:"hash_pubkey"` //immutable
	Packet      []byte    `db:"packet"`      // immutable
	Otl_hash    string    `db:"otl_hash"`    // mutable
	Req_time    time.Time `db:"req_time"`    // immutable
	Veri_time   time.Time `db:"veri_time"`   // mutable
	Expi_time   time.Time `db:"expi_time"`   //immutable
	Is_verified bool      `db:"is_verified"` // mutable
	Key_text    string    `db:"key_text"`    // immutable
	Pubkey_id   string    `db:"pubkey_id"`   //immutable
	Operation   int16     `db:"operation"`   //1 for add , 2 for delete
}

const ExpInDays int = 2

/*
op 1=add
op 2=delete
Verification process
			DONE 1. extract email ID from go.crypto/openpgp/packet struct userID
			DONE 2. lookup in to db for (e-mail,sha256_key) pair,if verified return true
			DONE 3. generate a random otl_hash for key
			DONE 4. add in to mail_verify db(e-mail,sha256_key,otl_hash,req_time,verify_time,verified,pubkey_packet,pub_key_ID)
			DONE	 5. call sendmail with otl and email
*/
func (w *Worker) Verify(eMail string, keytext string, req_Pubkey Pubkey, op int16) (isVerified bool, resultCode int) {

	log.Println(">>>>>>>>>>>>", eMail, "<<<<<<<<<<<<<<")
	log.Println("Verifier.go:Time of request", time.Now().Format(time.StampNano))
	/*TODO 2 and 4. verify in db
	If db has primary key pair
			--If verified && Req_time< ver_time
				return true
			--Else Email has not been verified yet but a request is already made
				--If now-(Req_time_old_request_from_db) > 2days
					delete old request and generate new request and send mail
				--Else
					return false
	Else db has no primary key pair
			Generate otl,save in db and send e-mail
			return false
	*/

	//Create and populate Veri_data
	verif_req_data := new(Veri_data)
	verif_req_data.Req_email = eMail
	verif_req_data.Packet = req_Pubkey.Packet
	verif_req_data.Key_text = keytext
	verif_req_data.Hash_pubkey = req_Pubkey.Sha256
	verif_req_data.Pubkey_id = req_Pubkey.Fingerprint()
	verif_req_data.Is_verified = false
	verif_req_data.Operation = op
	// otl_hash and 3 timestamps are still null
	ver, result_code := w.verifyOTL(verif_req_data)
	if result_code != 0 {
		if result_code == OTLNotFound {
			log.Println("OTL NOT FOUND!!")
			w.deleteOTL(verif_req_data) //Delete any previous OTL with same key hash
			if err := w.insertOTL(verif_req_data); err == nil {
				result_code = OTLNewOtlMade
			} else {
				result_code = ErrorSendingMail
			}

		} else if result_code == OTLExpired {
			log.Println("OTL Expired!!")
			w.handleExpiredOTL(verif_req_data)
			if err := w.insertOTL(verif_req_data); err == nil {
				result_code = OTLNewOtlMade
			}

		} else if result_code == OTLNotVerified {
			log.Println("OTL Not Verified!!")
		}
	} else {
		if ver {
			log.Println("OTL Exists and is verified")
			//verif_req_data.Is_verified = ver
		}
	}
	isVerified = ver
	resultCode = result_code
	return
}

const SaltSize = 16

func saltedHash(secret string) []byte {
	buf := make([]byte, SaltSize, SaltSize+(crypto.SHA256.Size()))
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		panic(fmt.Errorf("random read failed: %v", err))
	}
	h := crypto.SHA256.New()
	h.Write(buf)

	h.Write([]byte(secret))
	return h.Sum(buf)
}

func (w *Worker) insertOTL(veri *Veri_data) (err error) {
	/*
	    Else db has no primary key pair
	   			Generate otl,save in db and send e-mail
	   			return false
	*/
	//Check weather a key with same email exists on the server already,if yes replace it on OTL verification

	veri.Otl_hash = hex.EncodeToString(saltedHash(veri.Key_text + veri.Hash_pubkey))
	log.Println("verifier.go:otl_hash of above request is:", veri.Otl_hash)
	log.Println("verifier.go:Calling Send mail with", veri.Req_email, veri.Otl_hash)
	//OTL Format
	//http://host/prc/verify?otl=otlhash
	ownAuth, err1 := GetOwnAuthority()
	if err1 != nil {
		err = err1
		return
	}
	ownAddr := ownAuth.HkpAddr
	messageOTL := otlPrefix + ownAddr + otlMid + veri.Otl_hash
	var message string

	if veri.Operation == int16(1) {
		resKeys, err := w.LookupKeys(veri.Req_email, 2)
		if err == ErrKeyNotFound || len(resKeys) <= 0 { //No key found by Email
		} else { //Replcaes Key
			message = "	* A key with same email already exists on the server! Opening the following link will REPLACE the existing key having id = " +
				strings.ToUpper(resKeys[0].KeyId()) + ".		\n"
		}
	} else if veri.Operation == int16(2) {
		message = "	* Opening the following link will DELETE the existing key having id = " +
			strings.ToUpper(veri.Pubkey_id) + ".		\n"
	}

	message += "   " + messageOTL + "  "
	message += "\n	If you are not able to click above link then copy and paste it in the address bar of the browser to open it"
	err = SendEmail(veri.Req_email, subject, message)
	if err != nil {
		fmt.Println(err)
		log.Println(err)
		return
	} else {
		//Insert into DB
		veri.Req_time = time.Now()
		//func (t Time) AddDate(years int, months int, days int) Time
		veri.Expi_time = veri.Req_time.AddDate(0, 0, ExpInDays)
		//tx := w.db.MustBegin()

		if _, err = w.Begin(); err != nil {
			log.Println("Insert OTL:", err)
			return
		}

		w.tx.Execl(`INSERT INTO verify_email
		(req_email, hash_pubkey, packet, otl_hash,
		req_time,veri_time,expi_time,
		is_verified,key_text,pubkey_id,operation) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9,$10,$11)`,
			veri.Req_email, veri.Hash_pubkey, veri.Packet, veri.Otl_hash,
			veri.Req_time, veri.Veri_time, veri.Expi_time,
			veri.Is_verified, veri.Key_text, veri.Pubkey_id, veri.Operation)

		err = w.Commit()
		if err != nil {
			log.Println("Insert OTL:", err)
			return
		}
	}
	return
}

func (w *Worker) verifyOTL(veri *Veri_data) (is_verified bool, result_code int) {

	/*
		If db has primary key pair
				--If verified && Req_time< ver_time
					return true
				--Else Email has not been verified yet but a request is already made
					--If now-(Req_time_old_request_from_db) > 2days
						delete old request and generate new request and send mail
					--Else
						return false
	*/
	Temp_veri := []Veri_data{}

	w.db.Select(&Temp_veri, `SELECT * FROM verify_email
	WHERE hash_pubkey=$1
	AND (req_email=$2
	AND pubkey_id=$3 AND operation=$4)`, veri.Hash_pubkey, veri.Req_email, veri.Pubkey_id, veri.Operation)

	if len(Temp_veri) == 0 {
		result_code = OTLNotFound
		veri.Is_verified = false
	} else {
		log.Println("Sha256 matched!!", Temp_veri[0].Hash_pubkey == veri.Hash_pubkey)
		log.Println("key id matched!!", Temp_veri[0].Pubkey_id == veri.Pubkey_id)
		if Temp_veri[0].Hash_pubkey == veri.Hash_pubkey && Temp_veri[0].Pubkey_id == veri.Pubkey_id {
			*veri = Temp_veri[0]
			if veri.Expi_time.Before(time.Now()) {
				result_code = OTLExpired
				w.handleExpiredOTL(veri)
				veri.Is_verified = false
			} else if veri.Is_verified != true {
				result_code = OTLNotVerified
			}
		}
	}

	is_verified = veri.Is_verified
	return
}
func (w *Worker) handleExpiredOTL(veri *Veri_data) (err error) {
	//Delete row of hash_pubkey
	//tx := w.db.MustBegin()

	if _, err = w.Begin(); err != nil {
		log.Println("Handle Expire OTL:", err)
		return
	}
	w.tx.Execl(`DELETE FROM verify_email
				WHERE hash_pubkey = $1`, veri.Hash_pubkey)
	if err = w.Commit(); err != nil {
		log.Println("Handle Expire OTL:", err)

		return
	}

	//err = tx.Commit()

	return
}
func (w *Worker) deleteOTL(veri *Veri_data) (err error) {
	//Delete row of hash_pubkey
	//tx := w.db.MustBegin()

	if _, err = w.Begin(); err != nil {
		log.Println("Delete OTL:", err)
		return
	}

	w.tx.Execl(`DELETE FROM verify_email
				WHERE hash_pubkey = $1`, veri.Hash_pubkey)
	w.tx.Execl(`DELETE FROM verify_email
				WHERE pubkey_id = $1`, veri.Pubkey_id)
	//err = tx.Commit()
	if err = w.Commit(); err != nil {
		log.Println("Delete OTL:", err)
		return
	}

	return
}

/*		TODO: 1.handle otl clicks in otl_handler
//2.verify from mail_verify db
//3. Sign the key with servers key
//4. Store the signed key in tmp db
//3.then again call add with new key
*/

/*
const Cr_verify_email = `
CREATE TABLE IF NOT EXISTS verify_email (
-----------------------------------------------------------------------
-- Scope- and content-unique identifer
req_email TEXT NOT NULL,
-- Sha256 Hash of the key to check weather key has been changed since last request
hash_pubkey TEXT NOT NULL,
-- Binary contents of the OpenPGP packet
packet bytea NOT NULL,
-- otl_hash Salted Hash with random input
otl_hash TEXT NOT NULL,
-- Verification Request creation timestamp
req_time TIMESTAMP WITH TIME ZONE NOT NULL,
-- OTL Verification timestamp
veri_time TIMESTAMP WITH TIME ZONE,
-- OTL_Hash Expiration time (creation Time + 2 days)
expi_time TIMESTAMP WITH TIME ZONE NOT NULL,
--Weather verified or not Boolean
is_verified BOOLEAN NOT NULL,
-- armored text of hkp/add request
key_text TEXT NOT NULL,
-- Reference to the RFC 4880 ID (Fingerprint) of requested public key
pubkey_id TEXT NOT NULL,
----------------------------
PRIMARY KEY (hash_pubkey)

)`
*/

/*
<<<lookup>>>
rows, err := w.db.Queryx(fmt.Sprintf(`
SELECT uuid FROM openpgp_pubkey WHERE uuid %s
UNION
SELECT pubkey_uuid FROM openpgp_subkey WHERE uuid %s`, compareOp, compareOp), rKeyId)
	if err == sql.ErrNoRows {
		return nil, ErrKeyNotFound
	} else if err != nil {
		return
	}

<<< Insert>>>
tx := db.MustBegin()
    tx.Execl("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net")
tx.Commit()

    // Query the database, storing results in a []Person (wrapped in []interface{})
    people := []Person{}
    db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
    jason, john := people[0], people[1]

*/
<commit_msg>Added copyright<commit_after>// prc_verifier
/*
PRLPKS - OpenPGP Synchronized Key Server with Deletion
Copyright (c) 2014 Pruthvirajsinh Rajendrasinh Chauhan

PRLPKS is based heavily on hockeypuck(https://launchpad.net/hockeypuck) by Casey Marshall, copyright 2013(GNU GPL v3).

   This program 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.

*/
package openpgp

import (
	//"code.google.com/p/go.crypto/openpgp/armor"
	//"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
	//"github.com/jmoiron/sqlx"
	. "github.com/pruthvirajsinh/prlpks/errors"
	"io"
	"strings"
	"time"
)

const (
	OTLVerified         int = 1
	OTLNotFound         int = 2
	OTLExpired          int = 3
	OTLNotVerified      int = 4
	OTLAlereadyVerified int = 5
	OTLNewOtlMade       int = 6
	ErrorSendingMail    int = 7
)

const ( //To create Email
	subject   string = "PKS:Please verify the request"
	otlPrefix string = "http://"
	otlMid    string = "/prc/verify?otl="
)

type Veri_data struct {
	//Database values
	//otl_hash,req_email,req_time,verify_time,verified,req_pubkey
	Req_email   string    `db:"req_email"`   // immutable
	Hash_pubkey string    `db:"hash_pubkey"` //immutable
	Packet      []byte    `db:"packet"`      // immutable
	Otl_hash    string    `db:"otl_hash"`    // mutable
	Req_time    time.Time `db:"req_time"`    // immutable
	Veri_time   time.Time `db:"veri_time"`   // mutable
	Expi_time   time.Time `db:"expi_time"`   //immutable
	Is_verified bool      `db:"is_verified"` // mutable
	Key_text    string    `db:"key_text"`    // immutable
	Pubkey_id   string    `db:"pubkey_id"`   //immutable
	Operation   int16     `db:"operation"`   //1 for add , 2 for delete
}

const ExpInDays int = 2

/*
op 1=add
op 2=delete
Verification process
			DONE 1. extract email ID from go.crypto/openpgp/packet struct userID
			DONE 2. lookup in to db for (e-mail,sha256_key) pair,if verified return true
			DONE 3. generate a random otl_hash for key
			DONE 4. add in to mail_verify db(e-mail,sha256_key,otl_hash,req_time,verify_time,verified,pubkey_packet,pub_key_ID)
			DONE	 5. call sendmail with otl and email
*/
func (w *Worker) Verify(eMail string, keytext string, req_Pubkey Pubkey, op int16) (isVerified bool, resultCode int) {

	log.Println(">>>>>>>>>>>>", eMail, "<<<<<<<<<<<<<<")
	log.Println("Verifier.go:Time of request", time.Now().Format(time.StampNano))
	/*TODO 2 and 4. verify in db
	If db has primary key pair
			--If verified && Req_time< ver_time
				return true
			--Else Email has not been verified yet but a request is already made
				--If now-(Req_time_old_request_from_db) > 2days
					delete old request and generate new request and send mail
				--Else
					return false
	Else db has no primary key pair
			Generate otl,save in db and send e-mail
			return false
	*/

	//Create and populate Veri_data
	verif_req_data := new(Veri_data)
	verif_req_data.Req_email = eMail
	verif_req_data.Packet = req_Pubkey.Packet
	verif_req_data.Key_text = keytext
	verif_req_data.Hash_pubkey = req_Pubkey.Sha256
	verif_req_data.Pubkey_id = req_Pubkey.Fingerprint()
	verif_req_data.Is_verified = false
	verif_req_data.Operation = op
	// otl_hash and 3 timestamps are still null
	ver, result_code := w.verifyOTL(verif_req_data)
	if result_code != 0 {
		if result_code == OTLNotFound {
			log.Println("OTL NOT FOUND!!")
			w.deleteOTL(verif_req_data) //Delete any previous OTL with same key hash
			if err := w.insertOTL(verif_req_data); err == nil {
				result_code = OTLNewOtlMade
			} else {
				result_code = ErrorSendingMail
			}

		} else if result_code == OTLExpired {
			log.Println("OTL Expired!!")
			w.handleExpiredOTL(verif_req_data)
			if err := w.insertOTL(verif_req_data); err == nil {
				result_code = OTLNewOtlMade
			}

		} else if result_code == OTLNotVerified {
			log.Println("OTL Not Verified!!")
		}
	} else {
		if ver {
			log.Println("OTL Exists and is verified")
			//verif_req_data.Is_verified = ver
		}
	}
	isVerified = ver
	resultCode = result_code
	return
}

const SaltSize = 16

func saltedHash(secret string) []byte {
	buf := make([]byte, SaltSize, SaltSize+(crypto.SHA256.Size()))
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		panic(fmt.Errorf("random read failed: %v", err))
	}
	h := crypto.SHA256.New()
	h.Write(buf)

	h.Write([]byte(secret))
	return h.Sum(buf)
}

func (w *Worker) insertOTL(veri *Veri_data) (err error) {
	/*
	    Else db has no primary key pair
	   			Generate otl,save in db and send e-mail
	   			return false
	*/
	//Check weather a key with same email exists on the server already,if yes replace it on OTL verification

	veri.Otl_hash = hex.EncodeToString(saltedHash(veri.Key_text + veri.Hash_pubkey))
	log.Println("verifier.go:otl_hash of above request is:", veri.Otl_hash)
	log.Println("verifier.go:Calling Send mail with", veri.Req_email, veri.Otl_hash)
	//OTL Format
	//http://host/prc/verify?otl=otlhash
	ownAuth, err1 := GetOwnAuthority()
	if err1 != nil {
		err = err1
		return
	}
	ownAddr := ownAuth.HkpAddr
	messageOTL := otlPrefix + ownAddr + otlMid + veri.Otl_hash
	var message string

	if veri.Operation == int16(1) {
		resKeys, err := w.LookupKeys(veri.Req_email, 2)
		if err == ErrKeyNotFound || len(resKeys) <= 0 { //No key found by Email
		} else { //Replcaes Key
			message = "	* A key with same email already exists on the server! Opening the following link will REPLACE the existing key having id = " +
				strings.ToUpper(resKeys[0].KeyId()) + ".		\n"
		}
	} else if veri.Operation == int16(2) {
		message = "	* Opening the following link will DELETE the existing key having id = " +
			strings.ToUpper(veri.Pubkey_id) + ".		\n"
	}

	message += "   " + messageOTL + "  "
	message += "\n	If you are not able to click above link then copy and paste it in the address bar of the browser to open it"
	err = SendEmail(veri.Req_email, subject, message)
	if err != nil {
		fmt.Println(err)
		log.Println(err)
		return
	} else {
		//Insert into DB
		veri.Req_time = time.Now()
		//func (t Time) AddDate(years int, months int, days int) Time
		veri.Expi_time = veri.Req_time.AddDate(0, 0, ExpInDays)
		//tx := w.db.MustBegin()

		if _, err = w.Begin(); err != nil {
			log.Println("Insert OTL:", err)
			return
		}

		w.tx.Execl(`INSERT INTO verify_email
		(req_email, hash_pubkey, packet, otl_hash,
		req_time,veri_time,expi_time,
		is_verified,key_text,pubkey_id,operation) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9,$10,$11)`,
			veri.Req_email, veri.Hash_pubkey, veri.Packet, veri.Otl_hash,
			veri.Req_time, veri.Veri_time, veri.Expi_time,
			veri.Is_verified, veri.Key_text, veri.Pubkey_id, veri.Operation)

		err = w.Commit()
		if err != nil {
			log.Println("Insert OTL:", err)
			return
		}
	}
	return
}

func (w *Worker) verifyOTL(veri *Veri_data) (is_verified bool, result_code int) {

	/*
		If db has primary key pair
				--If verified && Req_time< ver_time
					return true
				--Else Email has not been verified yet but a request is already made
					--If now-(Req_time_old_request_from_db) > 2days
						delete old request and generate new request and send mail
					--Else
						return false
	*/
	Temp_veri := []Veri_data{}

	w.db.Select(&Temp_veri, `SELECT * FROM verify_email
	WHERE hash_pubkey=$1
	AND (req_email=$2
	AND pubkey_id=$3 AND operation=$4)`, veri.Hash_pubkey, veri.Req_email, veri.Pubkey_id, veri.Operation)

	if len(Temp_veri) == 0 {
		result_code = OTLNotFound
		veri.Is_verified = false
	} else {
		log.Println("Sha256 matched!!", Temp_veri[0].Hash_pubkey == veri.Hash_pubkey)
		log.Println("key id matched!!", Temp_veri[0].Pubkey_id == veri.Pubkey_id)
		if Temp_veri[0].Hash_pubkey == veri.Hash_pubkey && Temp_veri[0].Pubkey_id == veri.Pubkey_id {
			*veri = Temp_veri[0]
			if veri.Expi_time.Before(time.Now()) {
				result_code = OTLExpired
				w.handleExpiredOTL(veri)
				veri.Is_verified = false
			} else if veri.Is_verified != true {
				result_code = OTLNotVerified
			}
		}
	}

	is_verified = veri.Is_verified
	return
}
func (w *Worker) handleExpiredOTL(veri *Veri_data) (err error) {
	//Delete row of hash_pubkey
	//tx := w.db.MustBegin()

	if _, err = w.Begin(); err != nil {
		log.Println("Handle Expire OTL:", err)
		return
	}
	w.tx.Execl(`DELETE FROM verify_email
				WHERE hash_pubkey = $1`, veri.Hash_pubkey)
	if err = w.Commit(); err != nil {
		log.Println("Handle Expire OTL:", err)

		return
	}

	//err = tx.Commit()

	return
}
func (w *Worker) deleteOTL(veri *Veri_data) (err error) {
	//Delete row of hash_pubkey
	//tx := w.db.MustBegin()

	if _, err = w.Begin(); err != nil {
		log.Println("Delete OTL:", err)
		return
	}

	w.tx.Execl(`DELETE FROM verify_email
				WHERE hash_pubkey = $1`, veri.Hash_pubkey)
	w.tx.Execl(`DELETE FROM verify_email
				WHERE pubkey_id = $1`, veri.Pubkey_id)
	//err = tx.Commit()
	if err = w.Commit(); err != nil {
		log.Println("Delete OTL:", err)
		return
	}

	return
}

/*		TODO: 1.handle otl clicks in otl_handler
//2.verify from mail_verify db
//3. Sign the key with servers key
//4. Store the signed key in tmp db
//3.then again call add with new key
*/

/*
const Cr_verify_email = `
CREATE TABLE IF NOT EXISTS verify_email (
-----------------------------------------------------------------------
-- Scope- and content-unique identifer
req_email TEXT NOT NULL,
-- Sha256 Hash of the key to check weather key has been changed since last request
hash_pubkey TEXT NOT NULL,
-- Binary contents of the OpenPGP packet
packet bytea NOT NULL,
-- otl_hash Salted Hash with random input
otl_hash TEXT NOT NULL,
-- Verification Request creation timestamp
req_time TIMESTAMP WITH TIME ZONE NOT NULL,
-- OTL Verification timestamp
veri_time TIMESTAMP WITH TIME ZONE,
-- OTL_Hash Expiration time (creation Time + 2 days)
expi_time TIMESTAMP WITH TIME ZONE NOT NULL,
--Weather verified or not Boolean
is_verified BOOLEAN NOT NULL,
-- armored text of hkp/add request
key_text TEXT NOT NULL,
-- Reference to the RFC 4880 ID (Fingerprint) of requested public key
pubkey_id TEXT NOT NULL,
----------------------------
PRIMARY KEY (hash_pubkey)

)`
*/

/*
<<<lookup>>>
rows, err := w.db.Queryx(fmt.Sprintf(`
SELECT uuid FROM openpgp_pubkey WHERE uuid %s
UNION
SELECT pubkey_uuid FROM openpgp_subkey WHERE uuid %s`, compareOp, compareOp), rKeyId)
	if err == sql.ErrNoRows {
		return nil, ErrKeyNotFound
	} else if err != nil {
		return
	}

<<< Insert>>>
tx := db.MustBegin()
    tx.Execl("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net")
tx.Commit()

    // Query the database, storing results in a []Person (wrapped in []interface{})
    people := []Person{}
    db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
    jason, john := people[0], people[1]

*/
<|endoftext|>