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

// Package mdns implements mDNS plugin for discovery service.
//
// In order to support discovery of a specific vanadium service, an instance
// is advertised in two ways - one as a vanadium service and the other as a
// subtype of vanadium service.
//
// For example, a vanadium printer service is advertised as
//
//    v23._tcp.local.
//    _<printer_service_uuid>._sub._v23._tcp.local.
package mdns

import (
	"bytes"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"v.io/v23/context"
	"v.io/v23/discovery"

	idiscovery "v.io/x/ref/lib/discovery"

	"github.com/pborman/uuid"
	mdns "github.com/presotto/go-mdns-sd"
)

const (
	v23ServiceName    = "v23"
	serviceNameSuffix = "._sub._" + v23ServiceName

	// Use short attribute names due to the txt record size limit.
	attrName       = "_n"
	attrInterface  = "_i"
	attrAddrs      = "_a"
	attrEncryption = "_e"
	attrHash       = "_h"
	attrDirAddrs   = "_d"

	// The prefix for attribute names for encoded attachments.
	attrAttachmentPrefix = "__"

	// The prefix for attribute names for encoded large txt records.
	attrLargeTxtPrefix = "_x"

	// RFC 6763 limits each DNS txt record to 255 bytes and recommends to not have
	// the cumulative size be larger than 1300 bytes.
	//
	// TODO(jhahn): Figure out how to overcome this limit.
	maxTxtRecordLen       = 255
	maxTotalTxtRecordsLen = 1300
)

var (
	errMaxTxtRecordLenExceeded = errors.New("max txt record size exceeded")
)

type plugin struct {
	mdns      *mdns.MDNS
	adStopper *idiscovery.Trigger

	subscriptionRefreshTime time.Duration
	subscriptionWaitTime    time.Duration
	subscriptionMu          sync.Mutex
	subscription            map[string]subscription // GUARDED_BY(subscriptionMu)
}

type subscription struct {
	count            int
	lastSubscription time.Time
}

func interfaceNameToServiceName(interfaceName string) string {
	serviceUuid := idiscovery.NewServiceUUID(interfaceName)
	return uuid.UUID(serviceUuid).String() + serviceNameSuffix
}

func (p *plugin) Advertise(ctx *context.T, ad idiscovery.Advertisement, done func()) (err error) {
	serviceName := interfaceNameToServiceName(ad.Service.InterfaceName)
	// We use the instance uuid as the host name so that we can get the instance uuid
	// from the lost service instance, which has no txt records at all.
	hostName := encodeInstanceId(ad.Service.InstanceId)
	txt, err := createTxtRecords(&ad)
	if err != nil {
		done()
		return err
	}

	// Announce the service.
	err = p.mdns.AddService(serviceName, hostName, 0, txt...)
	if err != nil {
		done()
		return err
	}

	// Announce it as v23 service as well so that we can discover
	// all v23 services through mDNS.
	err = p.mdns.AddService(v23ServiceName, hostName, 0, txt...)
	if err != nil {
		done()
		return err
	}
	stop := func() {
		p.mdns.RemoveService(serviceName, hostName, 0, txt...)
		p.mdns.RemoveService(v23ServiceName, hostName, 0, txt...)
		done()
	}
	p.adStopper.Add(stop, ctx.Done())
	return nil
}

func (p *plugin) Scan(ctx *context.T, interfaceName string, ch chan<- idiscovery.Advertisement, done func()) error {
	var serviceName string
	if len(interfaceName) == 0 {
		serviceName = v23ServiceName
	} else {
		serviceName = interfaceNameToServiceName(interfaceName)
	}

	go func() {
		defer done()

		p.subscribeToService(serviceName)
		watcher, stopWatcher := p.mdns.ServiceMemberWatch(serviceName)
		defer func() {
			stopWatcher()
			p.unsubscribeFromService(serviceName)
		}()

		for {
			var service mdns.ServiceInstance
			select {
			case service = <-watcher:
			case <-time.After(p.subscriptionRefreshTime):
				p.refreshSubscription(serviceName)
				continue
			case <-ctx.Done():
				return
			}
			ad, err := createAdvertisement(service)
			if err != nil {
				ctx.Error(err)
				continue
			}
			select {
			case ch <- ad:
			case <-ctx.Done():
				return
			}
		}
	}()
	return nil
}

func (p *plugin) subscribeToService(serviceName string) {
	p.subscriptionMu.Lock()
	sub := p.subscription[serviceName]
	sub.count++
	p.subscription[serviceName] = sub
	p.subscriptionMu.Unlock()
	p.refreshSubscription(serviceName)
}

func (p *plugin) unsubscribeFromService(serviceName string) {
	p.subscriptionMu.Lock()
	sub := p.subscription[serviceName]
	sub.count--
	if sub.count == 0 {
		delete(p.subscription, serviceName)
		p.mdns.UnsubscribeFromService(serviceName)
	} else {
		p.subscription[serviceName] = sub
	}
	p.subscriptionMu.Unlock()
}

func (p *plugin) refreshSubscription(serviceName string) {
	p.subscriptionMu.Lock()
	sub, ok := p.subscription[serviceName]
	if !ok {
		p.subscriptionMu.Unlock()
		return
	}
	// Subscribe to the service again if we haven't refreshed in a while.
	if time.Since(sub.lastSubscription) > p.subscriptionRefreshTime {
		p.mdns.SubscribeToService(serviceName)
		// Wait a bit to learn about neighborhood.
		time.Sleep(p.subscriptionWaitTime)
		sub.lastSubscription = time.Now()
	}
	p.subscription[serviceName] = sub
	p.subscriptionMu.Unlock()
}

func createTxtRecords(ad *idiscovery.Advertisement) ([]string, error) {
	// Prepare a txt record with attributes and addresses to announce.
	txt := appendTxtRecord(nil, attrInterface, ad.Service.InterfaceName)
	if len(ad.Service.InstanceName) > 0 {
		txt = appendTxtRecord(txt, attrName, ad.Service.InstanceName)
	}
	if len(ad.Service.Addrs) > 0 {
		addrs := idiscovery.PackAddresses(ad.Service.Addrs)
		txt = appendTxtRecord(txt, attrAddrs, addrs)
	}
	if ad.EncryptionAlgorithm != idiscovery.NoEncryption {
		enc := idiscovery.PackEncryptionKeys(ad.EncryptionAlgorithm, ad.EncryptionKeys)
		txt = appendTxtRecord(txt, attrEncryption, enc)
	}
	for k, v := range ad.Service.Attrs {
		txt = appendTxtRecord(txt, k, v)
	}
	for k, v := range ad.Service.Attachments {
		txt = appendTxtRecord(txt, attrAttachmentPrefix+k, v)
	}
	txt = appendTxtRecord(txt, attrHash, ad.Hash)
	if len(ad.DirAddrs) > 0 {
		addrs := idiscovery.PackAddresses(ad.DirAddrs)
		txt = appendTxtRecord(txt, attrDirAddrs, addrs)
	}
	txt, err := maybeSplitLargeTXT(txt)
	if err != nil {
		return nil, err
	}
	n := 0
	for _, v := range txt {
		n += len(v)
		if n > maxTotalTxtRecordsLen {
			return nil, errMaxTxtRecordLenExceeded
		}
	}
	return txt, nil
}

func appendTxtRecord(txt []string, k string, v interface{}) []string {
	var buf bytes.Buffer
	buf.WriteString(k)
	buf.WriteByte('=')
	if s, ok := v.(string); ok {
		buf.WriteString(s)
	} else {
		buf.Write(v.([]byte))
	}
	kv := buf.String()
	txt = append(txt, kv)
	return txt
}

func createAdvertisement(service mdns.ServiceInstance) (idiscovery.Advertisement, error) {
	// Note that service.Name starts with a host name, which is the instance uuid.
	p := strings.SplitN(service.Name, ".", 2)
	if len(p) < 1 {
		return idiscovery.Advertisement{}, fmt.Errorf("invalid service name: %s", service.Name)
	}
	instanceId, err := decodeInstanceId(p[0])
	if err != nil {
		return idiscovery.Advertisement{}, fmt.Errorf("invalid host name: %v", err)
	}

	ad := idiscovery.Advertisement{Service: discovery.Service{InstanceId: instanceId}}
	if len(service.SrvRRs) == 0 && len(service.TxtRRs) == 0 {
		ad.Lost = true
		return ad, nil
	}

	ad.Service.Attrs = make(discovery.Attributes)
	ad.Service.Attachments = make(discovery.Attachments)
	for _, rr := range service.TxtRRs {
		txt, err := maybeJoinLargeTXT(rr.Txt)
		if err != nil {
			return idiscovery.Advertisement{}, err
		}

		for _, kv := range txt {
			p := strings.SplitN(kv, "=", 2)
			if len(p) != 2 {
				return idiscovery.Advertisement{}, fmt.Errorf("invalid txt record: %s", txt)
			}
			switch k, v := p[0], p[1]; k {
			case attrName:
				ad.Service.InstanceName = v
			case attrInterface:
				ad.Service.InterfaceName = v
			case attrAddrs:
				if ad.Service.Addrs, err = idiscovery.UnpackAddresses([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			case attrEncryption:
				if ad.EncryptionAlgorithm, ad.EncryptionKeys, err = idiscovery.UnpackEncryptionKeys([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			case attrHash:
				ad.Hash = []byte(v)
			case attrDirAddrs:
				if ad.DirAddrs, err = idiscovery.UnpackAddresses([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			default:
				if strings.HasPrefix(k, attrAttachmentPrefix) {
					ad.Service.Attachments[k[len(attrAttachmentPrefix):]] = []byte(v)
				} else {
					ad.Service.Attrs[k] = v
				}
			}
		}
	}
	return ad, nil
}

func New(host string) (idiscovery.Plugin, error) {
	return newWithLoopback(host, 0, false)
}

func newWithLoopback(host string, port int, loopback bool) (idiscovery.Plugin, error) {
	if len(host) == 0 {
		// go-mdns-sd doesn't answer when the host name is not set.
		// Assign a default one if not given.
		host = "v23()"
	}
	var v4addr, v6addr string
	if port > 0 {
		v4addr = fmt.Sprintf("224.0.0.251:%d", port)
		v6addr = fmt.Sprintf("[FF02::FB]:%d", port)
	}
	m, err := mdns.NewMDNS(host, v4addr, v6addr, loopback, 0)
	if err != nil {
		// The name may not have been unique. Try one more time with a unique
		// name. NewMDNS will replace the "()" with "(hardware mac address)".
		if len(host) > 0 && !strings.HasSuffix(host, "()") {
			m, err = mdns.NewMDNS(host+"()", "", "", loopback, 0)
		}
		if err != nil {
			return nil, err
		}
	}
	p := plugin{
		mdns:      m,
		adStopper: idiscovery.NewTrigger(),
		// TODO(jhahn): Figure out a good subscription refresh time.
		subscriptionRefreshTime: 15 * time.Second,
		subscription:            make(map[string]subscription),
	}
	if loopback {
		p.subscriptionWaitTime = 5 * time.Millisecond
	} else {
		p.subscriptionWaitTime = 50 * time.Millisecond
	}
	return &p, nil
}
<commit_msg>discovery: mdns - add suffix "()" to hostname "localhost"<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package mdns implements mDNS plugin for discovery service.
//
// In order to support discovery of a specific vanadium service, an instance
// is advertised in two ways - one as a vanadium service and the other as a
// subtype of vanadium service.
//
// For example, a vanadium printer service is advertised as
//
//    v23._tcp.local.
//    _<printer_service_uuid>._sub._v23._tcp.local.
package mdns

import (
	"bytes"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"v.io/v23/context"
	"v.io/v23/discovery"

	idiscovery "v.io/x/ref/lib/discovery"

	"github.com/pborman/uuid"
	mdns "github.com/presotto/go-mdns-sd"
)

const (
	v23ServiceName    = "v23"
	serviceNameSuffix = "._sub._" + v23ServiceName

	// Use short attribute names due to the txt record size limit.
	attrName       = "_n"
	attrInterface  = "_i"
	attrAddrs      = "_a"
	attrEncryption = "_e"
	attrHash       = "_h"
	attrDirAddrs   = "_d"

	// The prefix for attribute names for encoded attachments.
	attrAttachmentPrefix = "__"

	// The prefix for attribute names for encoded large txt records.
	attrLargeTxtPrefix = "_x"

	// RFC 6763 limits each DNS txt record to 255 bytes and recommends to not have
	// the cumulative size be larger than 1300 bytes.
	//
	// TODO(jhahn): Figure out how to overcome this limit.
	maxTxtRecordLen       = 255
	maxTotalTxtRecordsLen = 1300
)

var (
	errMaxTxtRecordLenExceeded = errors.New("max txt record size exceeded")
)

type plugin struct {
	mdns      *mdns.MDNS
	adStopper *idiscovery.Trigger

	subscriptionRefreshTime time.Duration
	subscriptionWaitTime    time.Duration
	subscriptionMu          sync.Mutex
	subscription            map[string]subscription // GUARDED_BY(subscriptionMu)
}

type subscription struct {
	count            int
	lastSubscription time.Time
}

func interfaceNameToServiceName(interfaceName string) string {
	serviceUuid := idiscovery.NewServiceUUID(interfaceName)
	return uuid.UUID(serviceUuid).String() + serviceNameSuffix
}

func (p *plugin) Advertise(ctx *context.T, ad idiscovery.Advertisement, done func()) (err error) {
	serviceName := interfaceNameToServiceName(ad.Service.InterfaceName)
	// We use the instance uuid as the host name so that we can get the instance uuid
	// from the lost service instance, which has no txt records at all.
	hostName := encodeInstanceId(ad.Service.InstanceId)
	txt, err := createTxtRecords(&ad)
	if err != nil {
		done()
		return err
	}

	// Announce the service.
	err = p.mdns.AddService(serviceName, hostName, 0, txt...)
	if err != nil {
		done()
		return err
	}

	// Announce it as v23 service as well so that we can discover
	// all v23 services through mDNS.
	err = p.mdns.AddService(v23ServiceName, hostName, 0, txt...)
	if err != nil {
		done()
		return err
	}
	stop := func() {
		p.mdns.RemoveService(serviceName, hostName, 0, txt...)
		p.mdns.RemoveService(v23ServiceName, hostName, 0, txt...)
		done()
	}
	p.adStopper.Add(stop, ctx.Done())
	return nil
}

func (p *plugin) Scan(ctx *context.T, interfaceName string, ch chan<- idiscovery.Advertisement, done func()) error {
	var serviceName string
	if len(interfaceName) == 0 {
		serviceName = v23ServiceName
	} else {
		serviceName = interfaceNameToServiceName(interfaceName)
	}

	go func() {
		defer done()

		p.subscribeToService(serviceName)
		watcher, stopWatcher := p.mdns.ServiceMemberWatch(serviceName)
		defer func() {
			stopWatcher()
			p.unsubscribeFromService(serviceName)
		}()

		for {
			var service mdns.ServiceInstance
			select {
			case service = <-watcher:
			case <-time.After(p.subscriptionRefreshTime):
				p.refreshSubscription(serviceName)
				continue
			case <-ctx.Done():
				return
			}
			ad, err := createAdvertisement(service)
			if err != nil {
				ctx.Error(err)
				continue
			}
			select {
			case ch <- ad:
			case <-ctx.Done():
				return
			}
		}
	}()
	return nil
}

func (p *plugin) subscribeToService(serviceName string) {
	p.subscriptionMu.Lock()
	sub := p.subscription[serviceName]
	sub.count++
	p.subscription[serviceName] = sub
	p.subscriptionMu.Unlock()
	p.refreshSubscription(serviceName)
}

func (p *plugin) unsubscribeFromService(serviceName string) {
	p.subscriptionMu.Lock()
	sub := p.subscription[serviceName]
	sub.count--
	if sub.count == 0 {
		delete(p.subscription, serviceName)
		p.mdns.UnsubscribeFromService(serviceName)
	} else {
		p.subscription[serviceName] = sub
	}
	p.subscriptionMu.Unlock()
}

func (p *plugin) refreshSubscription(serviceName string) {
	p.subscriptionMu.Lock()
	sub, ok := p.subscription[serviceName]
	if !ok {
		p.subscriptionMu.Unlock()
		return
	}
	// Subscribe to the service again if we haven't refreshed in a while.
	if time.Since(sub.lastSubscription) > p.subscriptionRefreshTime {
		p.mdns.SubscribeToService(serviceName)
		// Wait a bit to learn about neighborhood.
		time.Sleep(p.subscriptionWaitTime)
		sub.lastSubscription = time.Now()
	}
	p.subscription[serviceName] = sub
	p.subscriptionMu.Unlock()
}

func createTxtRecords(ad *idiscovery.Advertisement) ([]string, error) {
	// Prepare a txt record with attributes and addresses to announce.
	txt := appendTxtRecord(nil, attrInterface, ad.Service.InterfaceName)
	if len(ad.Service.InstanceName) > 0 {
		txt = appendTxtRecord(txt, attrName, ad.Service.InstanceName)
	}
	if len(ad.Service.Addrs) > 0 {
		addrs := idiscovery.PackAddresses(ad.Service.Addrs)
		txt = appendTxtRecord(txt, attrAddrs, addrs)
	}
	if ad.EncryptionAlgorithm != idiscovery.NoEncryption {
		enc := idiscovery.PackEncryptionKeys(ad.EncryptionAlgorithm, ad.EncryptionKeys)
		txt = appendTxtRecord(txt, attrEncryption, enc)
	}
	for k, v := range ad.Service.Attrs {
		txt = appendTxtRecord(txt, k, v)
	}
	for k, v := range ad.Service.Attachments {
		txt = appendTxtRecord(txt, attrAttachmentPrefix+k, v)
	}
	txt = appendTxtRecord(txt, attrHash, ad.Hash)
	if len(ad.DirAddrs) > 0 {
		addrs := idiscovery.PackAddresses(ad.DirAddrs)
		txt = appendTxtRecord(txt, attrDirAddrs, addrs)
	}
	txt, err := maybeSplitLargeTXT(txt)
	if err != nil {
		return nil, err
	}
	n := 0
	for _, v := range txt {
		n += len(v)
		if n > maxTotalTxtRecordsLen {
			return nil, errMaxTxtRecordLenExceeded
		}
	}
	return txt, nil
}

func appendTxtRecord(txt []string, k string, v interface{}) []string {
	var buf bytes.Buffer
	buf.WriteString(k)
	buf.WriteByte('=')
	if s, ok := v.(string); ok {
		buf.WriteString(s)
	} else {
		buf.Write(v.([]byte))
	}
	kv := buf.String()
	txt = append(txt, kv)
	return txt
}

func createAdvertisement(service mdns.ServiceInstance) (idiscovery.Advertisement, error) {
	// Note that service.Name starts with a host name, which is the instance uuid.
	p := strings.SplitN(service.Name, ".", 2)
	if len(p) < 1 {
		return idiscovery.Advertisement{}, fmt.Errorf("invalid service name: %s", service.Name)
	}
	instanceId, err := decodeInstanceId(p[0])
	if err != nil {
		return idiscovery.Advertisement{}, fmt.Errorf("invalid host name: %v", err)
	}

	ad := idiscovery.Advertisement{Service: discovery.Service{InstanceId: instanceId}}
	if len(service.SrvRRs) == 0 && len(service.TxtRRs) == 0 {
		ad.Lost = true
		return ad, nil
	}

	ad.Service.Attrs = make(discovery.Attributes)
	ad.Service.Attachments = make(discovery.Attachments)
	for _, rr := range service.TxtRRs {
		txt, err := maybeJoinLargeTXT(rr.Txt)
		if err != nil {
			return idiscovery.Advertisement{}, err
		}

		for _, kv := range txt {
			p := strings.SplitN(kv, "=", 2)
			if len(p) != 2 {
				return idiscovery.Advertisement{}, fmt.Errorf("invalid txt record: %s", txt)
			}
			switch k, v := p[0], p[1]; k {
			case attrName:
				ad.Service.InstanceName = v
			case attrInterface:
				ad.Service.InterfaceName = v
			case attrAddrs:
				if ad.Service.Addrs, err = idiscovery.UnpackAddresses([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			case attrEncryption:
				if ad.EncryptionAlgorithm, ad.EncryptionKeys, err = idiscovery.UnpackEncryptionKeys([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			case attrHash:
				ad.Hash = []byte(v)
			case attrDirAddrs:
				if ad.DirAddrs, err = idiscovery.UnpackAddresses([]byte(v)); err != nil {
					return idiscovery.Advertisement{}, err
				}
			default:
				if strings.HasPrefix(k, attrAttachmentPrefix) {
					ad.Service.Attachments[k[len(attrAttachmentPrefix):]] = []byte(v)
				} else {
					ad.Service.Attrs[k] = v
				}
			}
		}
	}
	return ad, nil
}

func New(host string) (idiscovery.Plugin, error) {
	return newWithLoopback(host, 0, false)
}

func newWithLoopback(host string, port int, loopback bool) (idiscovery.Plugin, error) {
	switch {
	case len(host) == 0:
		// go-mdns-sd doesn't answer when the host name is not set.
		// Assign a default one if not given.
		host = "v23()"
	case host == "localhost":
		// localhost is a default host name in many places.
		// Add a suffix for uniqueness.
		host += "()"
	}
	var v4addr, v6addr string
	if port > 0 {
		v4addr = fmt.Sprintf("224.0.0.251:%d", port)
		v6addr = fmt.Sprintf("[FF02::FB]:%d", port)
	}
	m, err := mdns.NewMDNS(host, v4addr, v6addr, loopback, 0)
	if err != nil {
		// The name may not have been unique. Try one more time with a unique
		// name. NewMDNS will replace the "()" with "(hardware mac address)".
		if len(host) > 0 && !strings.HasSuffix(host, "()") {
			m, err = mdns.NewMDNS(host+"()", "", "", loopback, 0)
		}
		if err != nil {
			return nil, err
		}
	}
	p := plugin{
		mdns:      m,
		adStopper: idiscovery.NewTrigger(),
		// TODO(jhahn): Figure out a good subscription refresh time.
		subscriptionRefreshTime: 15 * time.Second,
		subscription:            make(map[string]subscription),
	}
	if loopback {
		p.subscriptionWaitTime = 5 * time.Millisecond
	} else {
		p.subscriptionWaitTime = 50 * time.Millisecond
	}
	return &p, nil
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/consts"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	return ast.Convert(func(x interface{}) interface{} {
		switch x := x.(type) {
		case ast.LetFunction:
			ls := make([]interface{}, 0, len(x.Lets()))

			for _, l := range x.Lets() {
				l := d.desugar(l)
				ls = append(ls, append(d.takeLets(), l)...)
			}

			b := d.desugar(x.Body())

			return ast.NewLetFunction(
				x.Name(),
				x.Signature(),
				append(ls, d.takeLets()...),
				b,
				x.DebugInfo())
		case ast.Match:
			cs := make([]ast.MatchCase, 0, len(x.Cases()))

			for _, c := range x.Cases() {
				cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
			}

			return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
		}

		return nil
	}, x)
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym()
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym(), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym()
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym(),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		switch x {
		case consts.Names.EmptyList:
			return listPattern
		case consts.Names.EmptyDictionary:
			return dictPattern
		}

		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return listPattern
		case consts.Names.DictionaryFunction:
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return ok
		case consts.Names.DictionaryFunction:
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)
	emptyCase := (interface{})(nil)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			emptyCase = c.Value()
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.ListFunction,
				ast.NewArguments(ps[1:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if cs := cs[i+1:]; len(cs) > 0 {
				dc = d.desugarListCases(list, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				first,
				v,
				d.desugarListCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	r := d.desugarCases(first, cs, dc)

	if emptyCase == nil {
		return r
	}

	return d.resultApp("$if", app("$=", list, consts.Names.EmptyList), emptyCase, r)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, consts.Names.EmptyDictionary), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	rest := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.DictionaryFunction,
				ast.NewArguments(ps[2:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if cs := cs[i+1:]; len(cs) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				value,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.value, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) defaultCaseOfGeneralNamePattern(v, p, body, dc interface{}) interface{} {
	switch getPatternType(p) {
	case namePattern:
		return body
	case listPattern:
		return d.ifType(v, "list", body, dc)
	case dictPattern:
		return d.ifType(v, "dict", body, dc)
	}

	panic("Unreachable")
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<commit_msg>Revert "Mark empty lists and dictionaries as list or dictionary"<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/consts"
	"github.com/tisp-lang/tisp/src/lib/debug"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type desugarer struct {
	letBoundNames, lets []interface{}
}

func newDesugarer() *desugarer {
	return &desugarer{nil, nil}
}

func (d *desugarer) desugar(x interface{}) interface{} {
	return ast.Convert(func(x interface{}) interface{} {
		switch x := x.(type) {
		case ast.LetFunction:
			ls := make([]interface{}, 0, len(x.Lets()))

			for _, l := range x.Lets() {
				l := d.desugar(l)
				ls = append(ls, append(d.takeLets(), l)...)
			}

			b := d.desugar(x.Body())

			return ast.NewLetFunction(
				x.Name(),
				x.Signature(),
				append(ls, d.takeLets()...),
				b,
				x.DebugInfo())
		case ast.Match:
			cs := make([]ast.MatchCase, 0, len(x.Cases()))

			for _, c := range x.Cases() {
				cs = append(cs, renameBoundNamesInCase(ast.NewMatchCase(c.Pattern(), d.desugar(c.Value()))))
			}

			return d.resultApp(d.createMatchFunction(cs), d.desugar(x.Value()))
		}

		return nil
	}, x)
}

func (d *desugarer) takeLets() []interface{} {
	ls := append(d.letBoundNames, d.lets...)
	d.letBoundNames = nil
	d.lets = nil
	return ls
}

func (d *desugarer) letTempVar(v interface{}) string {
	s := gensym.GenSym()
	d.lets = append(d.lets, ast.NewLetVar(s, v))
	return s
}

func (d *desugarer) bindName(p interface{}, v interface{}) string {
	s := generalNamePatternToName(p)
	d.letBoundNames = append(d.letBoundNames, ast.NewLetVar(s, v))
	return s
}

// matchedApp applies a function to arguments and creates a matched value of
// match expression.
func (d *desugarer) matchedApp(f interface{}, args ...interface{}) string {
	return d.bindName(gensym.GenSym(), app(f, args...))
}

// resultApp applies a function to arguments and creates a result value of match
// expression.
func (d *desugarer) resultApp(f interface{}, args ...interface{}) string {
	return d.letTempVar(app(f, args...))
}

func (d *desugarer) createMatchFunction(cs []ast.MatchCase) interface{} {
	arg := gensym.GenSym()
	body := d.desugarCases(arg, cs, "$matchError")

	f := ast.NewLetFunction(
		gensym.GenSym(),
		ast.NewSignature([]string{arg}, nil, "", nil, nil, ""),
		d.takeLets(),
		body,
		debug.NewGoInfo(0))

	d.lets = append(d.lets, f)

	return f.Name()
}

func (d *desugarer) desugarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	css := groupCases(cs)

	if cs, ok := css[namePattern]; ok {
		c := cs[0]
		d.bindName(c.Pattern().(string), v)
		dc = c.Value()
	}

	ks := []ast.SwitchCase{}

	if cs, ok := css[listPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"list\"", d.desugarListCases(v, cs, dc)))
	}

	if cs, ok := css[dictPattern]; ok {
		ks = append(ks, ast.NewSwitchCase("\"dict\"", d.desugarDictCases(v, cs, dc)))
	}

	if cs, ok := css[scalarPattern]; ok {
		dc = d.desugarScalarCases(v, cs, dc)
	}

	return newSwitch(d.resultApp("$typeOf", v), ks, dc)
}

func groupCases(cs []ast.MatchCase) map[patternType][]ast.MatchCase {
	css := map[patternType][]ast.MatchCase{}

	for i, c := range cs {
		t := getPatternType(c.Pattern())

		if t == namePattern && i < len(cs)-1 {
			panic("A wildcard pattern is found while some patterns are left")
		}

		css[t] = append(css[t], c)
	}

	return css
}

func getPatternType(p interface{}) patternType {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return scalarPattern
		}

		return namePattern
	case ast.App:
		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return listPattern
		case consts.Names.DictionaryFunction:
			return dictPattern
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func isGeneralNamePattern(p interface{}) bool {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return false
		}

		return true
	case ast.App:
		ps := x.Arguments().Positionals()
		ok := len(ps) == 1 && ps[0].Expanded()

		switch x.Function().(string) {
		case consts.Names.ListFunction:
			return ok
		case consts.Names.DictionaryFunction:
			return ok
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}

func (d *desugarer) desugarListCases(list interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		first interface{}
		cases []ast.MatchCase
	}

	gs := []group{}
	first := d.matchedApp("$first", list)
	rest := d.matchedApp("$rest", list)
	emptyCase := (interface{})(nil)

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			emptyCase = c.Value()
			continue
		}

		v := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(v.(string), list)
			dc = c.Value()
			break
		}

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.ListFunction,
				ast.NewArguments(ps[1:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, first)

			if cs := cs[i+1:]; len(cs) > 0 {
				dc = d.desugarListCases(list, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				first,
				v,
				d.desugarListCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.first) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.first, d.desugarCases(rest, g.cases, dc)))
	}

	r := d.desugarCases(first, cs, dc)

	if emptyCase == nil {
		return r
	}

	return d.resultApp("$if", app("$=", list, consts.Names.EmptyList), emptyCase, r)
}

func (d *desugarer) ifType(v interface{}, t string, then, els interface{}) interface{} {
	return d.resultApp("$if", app("$=", app("$typeOf", v), "\""+t+"\""), then, els)
}

func (d *desugarer) desugarDictCases(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		key   interface{}
		cases []ast.MatchCase
	}

	gs := []group{}

	for _, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()

		if len(ps) == 0 {
			dc = d.resultApp("$if", app("$=", dict, consts.Names.EmptyDictionary), c.Value(), dc)
			continue
		}

		k := ps[0].Value()

		if ps[0].Expanded() {
			d.bindName(k.(string), dict)
			dc = c.Value()
			break
		}

		g := group{k, []ast.MatchCase{c}}

		if len(gs) == 0 {
			gs = append(gs, g)
		} else if last := len(gs) - 1; equalPatterns(g.key, gs[last].key) {
			gs[last].cases = append(gs[last].cases, c)
		} else {
			gs = append(gs, g)
		}
	}

	for i := len(gs) - 1; i >= 0; i-- {
		g := gs[i]
		dc = d.resultApp("$if",
			app("$include", dict, g.key),
			d.desugarDictCasesOfSameKey(dict, g.cases, dc),
			dc)
	}

	return dc
}

func (d *desugarer) desugarDictCasesOfSameKey(dict interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	type group struct {
		value interface{}
		cases []ast.MatchCase
	}

	key := cs[0].Pattern().(ast.App).Arguments().Positionals()[0].Value()
	value := d.matchedApp(dict, key)
	rest := d.matchedApp("delete", dict, key)
	gs := []group{}

	for i, c := range cs {
		ps := c.Pattern().(ast.App).Arguments().Positionals()
		v := ps[1].Value()

		c = ast.NewMatchCase(
			ast.NewApp(
				consts.Names.DictionaryFunction,
				ast.NewArguments(ps[2:], nil, nil),
				debug.NewGoInfo(0)),
			c.Value())

		if isGeneralNamePattern(v) {
			d.bindName(v, value)

			if cs := cs[i+1:]; len(cs) != 0 {
				dc = d.desugarDictCasesOfSameKey(dict, cs, dc)
			}

			dc = d.defaultCaseOfGeneralNamePattern(
				value,
				v,
				d.desugarCases(rest, []ast.MatchCase{c}, dc),
				dc)
			break
		}

		groupExist := false

		for i, g := range gs {
			if equalPatterns(v, g.value) {
				groupExist = true
				gs[i].cases = append(gs[i].cases, c)
			}
		}

		if !groupExist {
			gs = append(gs, group{v, []ast.MatchCase{c}})
		}
	}

	cs = make([]ast.MatchCase, 0, len(gs))
	dc = d.letTempVar(dc)

	for _, g := range gs {
		cs = append(cs, ast.NewMatchCase(g.value, d.desugarCases(rest, g.cases, dc)))
	}

	return d.desugarCases(value, cs, dc)
}

func (d *desugarer) defaultCaseOfGeneralNamePattern(v, p, body, dc interface{}) interface{} {
	switch getPatternType(p) {
	case namePattern:
		return body
	case listPattern:
		return d.ifType(v, "list", body, dc)
	case dictPattern:
		return d.ifType(v, "dict", body, dc)
	}

	panic("Unreachable")
}

func (d *desugarer) desugarScalarCases(v interface{}, cs []ast.MatchCase, dc interface{}) interface{} {
	ks := []ast.SwitchCase{}

	for _, c := range cs {
		ks = append(ks, ast.NewSwitchCase(c.Pattern().(string), c.Value()))
	}

	return newSwitch(v, ks, dc)
}

func renameBoundNamesInCase(c ast.MatchCase) ast.MatchCase {
	p, ns := newPatternRenamer().rename(c.Pattern())
	return ast.NewMatchCase(p, newValueRenamer(ns).rename(c.Value()))
}

func equalPatterns(p, q interface{}) bool {
	switch x := p.(type) {
	case string:
		y, ok := q.(string)

		if !ok {
			return false
		}

		return x == y
	case ast.App:
		y, ok := q.(ast.App)

		if !ok ||
			x.Function().(string) != y.Function().(string) ||
			len(x.Arguments().Positionals()) != len(y.Arguments().Positionals()) {
			return false
		}

		for i := range x.Arguments().Positionals() {
			p := x.Arguments().Positionals()[i]
			q := y.Arguments().Positionals()[i]

			if p.Expanded() != q.Expanded() || !equalPatterns(p.Value(), q.Value()) {
				return false
			}
		}

		return true
	}

	panic(fmt.Errorf("Invalid pattern: %#v, %#v", p, q))
}

func generalNamePatternToName(p interface{}) string {
	switch x := p.(type) {
	case string:
		return x
	case ast.App:
		if ps := x.Arguments().Positionals(); len(ps) == 1 && ps[0].Expanded() {
			return ps[0].Value().(string)
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/x509"
	"strings"
	"time"
)

func datesAffected(expiry time.Time) (affected affectedStages, err error) {

	dateJan2016, dateJun2016, dateJan2017, err := getDates()
	if err != nil {
		return
	}

	// Do stage 1 checks (Chrome 39)
	//	if certificate expires on or after 2017-01-01
	//		- secure but with minor errors
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome39.MinorErrors = true
	}

	// Do stage 2 checks (Chrome 40)
	//	if certificate expires between 2016-06-01 to 2016-12-31
	//		- secure but with minor errors
	//	if certificate expires after 2017-01-01
	//		- neutral, lacking security
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome40.NoSecurity = true
	} else if equalOrAfter(expiry, dateJun2016) {
		affected.Expiry = true
		affected.Chrome40.MinorErrors = true
	}

	// Do stage 3 checks (Chrome 41)
	//	if certificate expires between 2016-01-01 to 2016-12-31
	//		- secure but with minor errors
	//	if certificate expires after 2017-01-01
	//		- insecure error
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome41.Insecure = true
	} else if equalOrAfter(expiry, dateJan2016) {
		affected.Expiry = true
		affected.Chrome41.MinorErrors = true
	}

	return

}

func analyseCerts(certs []*x509.Certificate, affected *affectedStages) {

	for _, cert := range certs {
		summary := certificate{}

		if !isRootCA(cert) {
			// Ignore root certificate as no-one trusts the signature itself
			affected.SHA1, summary.SigAlg = certSigAlg(cert)
		}

		summary.ExpiryDate = cert.NotAfter.Format("2006-01-02")
		if len(cert.DNSNames) == 0 {
			summary.ValidFor = cert.Subject.CommonName
		} else {
			summary.ValidFor = strings.Join(cert.DNSNames, ", ")
		}

		if !cert.IsCA {
			affected.Certificate = summary
		} else if isRootCA(cert) {
			affected.RootCertificate = summary
		} else {
			affected.Intermediates = append(affected.Intermediates, summary)
		}

	}

}

func certSigAlg(cert *x509.Certificate) (sha1 bool, sigAlg string) {

	switch cert.SignatureAlgorithm {
	case x509.SHA1WithRSA:
		sigAlg = "SHA1"
		sha1 = true
	case x509.DSAWithSHA1:
		sigAlg = "SHA1"
		sha1 = true
	case x509.ECDSAWithSHA1:
		sigAlg = "SHA1"
		sha1 = true
	case x509.MD5WithRSA:
		sigAlg = "MD5"
	case x509.SHA256WithRSA:
		sigAlg = "SHA256"
	case x509.SHA384WithRSA:
		sigAlg = "SHA384"
	case x509.SHA512WithRSA:
		sigAlg = "SHA512"
	case x509.DSAWithSHA256:
		sigAlg = "SHA256"
	case x509.ECDSAWithSHA256:
		sigAlg = "SHA256"
	case x509.ECDSAWithSHA384:
		sigAlg = "SHA384"
	case x509.ECDSAWithSHA512:
		sigAlg = "SHA512"
	default:
		sigAlg = "Unknown"
	}

	return

}

func isRootCA(cert *x509.Certificate) bool {
	if !cert.IsCA {
		// Ignore self signed certificates
		return false
	}

	if cert.Issuer.CommonName == cert.Subject.CommonName {
		// One day, actually check if the cert is actually signing itself
		return true
	}
	return false
}
<commit_msg>Fix bug where only the last certificate was used to determine whether a site is affected<commit_after>package main

import (
	"crypto/x509"
	"strings"
	"time"
)

func datesAffected(expiry time.Time) (affected affectedStages, err error) {

	dateJan2016, dateJun2016, dateJan2017, err := getDates()
	if err != nil {
		return
	}

	// Do stage 1 checks (Chrome 39)
	//	if certificate expires on or after 2017-01-01
	//		- secure but with minor errors
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome39.MinorErrors = true
	}

	// Do stage 2 checks (Chrome 40)
	//	if certificate expires between 2016-06-01 to 2016-12-31
	//		- secure but with minor errors
	//	if certificate expires after 2017-01-01
	//		- neutral, lacking security
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome40.NoSecurity = true
	} else if equalOrAfter(expiry, dateJun2016) {
		affected.Expiry = true
		affected.Chrome40.MinorErrors = true
	}

	// Do stage 3 checks (Chrome 41)
	//	if certificate expires between 2016-01-01 to 2016-12-31
	//		- secure but with minor errors
	//	if certificate expires after 2017-01-01
	//		- insecure error
	if equalOrAfter(expiry, dateJan2017) {
		affected.Expiry = true
		affected.Chrome41.Insecure = true
	} else if equalOrAfter(expiry, dateJan2016) {
		affected.Expiry = true
		affected.Chrome41.MinorErrors = true
	}

	return

}

func analyseCerts(certs []*x509.Certificate, affected *affectedStages) {

	for _, cert := range certs {
		summary := certificate{}
		hasSHA1 := false

		if !isRootCA(cert) {
			// Ignore root certificate as no-one trusts the signature itself
			hasSHA1, summary.SigAlg = certSigAlg(cert)

			if hasSHA1 {
				affected.SHA1 = true
			}
		}

		summary.ExpiryDate = cert.NotAfter.Format("2006-01-02")
		if len(cert.DNSNames) == 0 {
			summary.ValidFor = cert.Subject.CommonName
		} else {
			summary.ValidFor = strings.Join(cert.DNSNames, ", ")
		}

		if !cert.IsCA {
			affected.Certificate = summary
		} else if isRootCA(cert) {
			affected.RootCertificate = summary
		} else {
			affected.Intermediates = append(affected.Intermediates, summary)
		}

	}

}

func certSigAlg(cert *x509.Certificate) (sha1 bool, sigAlg string) {

	switch cert.SignatureAlgorithm {
	case x509.SHA1WithRSA:
		sigAlg = "SHA1"
		sha1 = true
	case x509.DSAWithSHA1:
		sigAlg = "SHA1"
		sha1 = true
	case x509.ECDSAWithSHA1:
		sigAlg = "SHA1"
		sha1 = true
	case x509.MD5WithRSA:
		sigAlg = "MD5"
	case x509.SHA256WithRSA:
		sigAlg = "SHA256"
	case x509.SHA384WithRSA:
		sigAlg = "SHA384"
	case x509.SHA512WithRSA:
		sigAlg = "SHA512"
	case x509.DSAWithSHA256:
		sigAlg = "SHA256"
	case x509.ECDSAWithSHA256:
		sigAlg = "SHA256"
	case x509.ECDSAWithSHA384:
		sigAlg = "SHA384"
	case x509.ECDSAWithSHA512:
		sigAlg = "SHA512"
	default:
		sigAlg = "Unknown"
	}

	return

}

func isRootCA(cert *x509.Certificate) bool {
	if !cert.IsCA {
		// Ignore self signed certificates
		return false
	}

	if cert.Issuer.CommonName == cert.Subject.CommonName {
		// One day, actually check if the cert is actually signing itself
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepSourceAMIInfo extracts critical information from the source AMI
// that is used throughout the AMI creation process.
//
// Produces:
//   source_image *ec2.Image - the source AMI info
type StepSourceAMIInfo struct {
	SourceAmi          string
	EnhancedNetworking bool
	AmiFilters         AmiFilterOptions
}

// Build a slice of AMI filter options from the filters provided.
func buildAmiFilters(input map[*string]*string) []*ec2.Filter {
	var filters []*ec2.Filter
	for k, v := range input {
		filters = append(filters, &ec2.Filter{
			Name:   k,
			Values: []*string{v},
		})
	}
	return filters
}

type imageSort []*ec2.Image

func (a imageSort) Len() int      { return len(a) }
func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a imageSort) Less(i, j int) bool {
	itime, _ := time.Parse(time.RFC3339, *a[i].CreationDate)
	jtime, _ := time.Parse(time.RFC3339, *a[j].CreationDate)
	return itime.Unix() < jtime.Unix()
}

// Returns the most recent AMI out of a slice of images.
func mostRecentAmi(images []*ec2.Image) *ec2.Image {
	sortedImages := images
	sort.Sort(imageSort(sortedImages))
	return sortedImages[len(sortedImages)-1]
}

func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	params := &ec2.DescribeImagesInput{}

	if s.SourceAmi != "" {
		params.ImageIds = []*string{&s.SourceAmi}
	}

	// We have filters to apply
	if len(s.AmiFilters.Filters) > 0 {
		params.Filters = buildAmiFilters(s.AmiFilters.Filters)
	}

	log.Printf("Using AMI Filters %v", params)
	imageResp, err := ec2conn.DescribeImages(params)
	if err != nil {
		err := fmt.Errorf("Error querying AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if len(imageResp.Images) == 0 {
		err := fmt.Errorf("No AMI was found matching filters: %v", params)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if len(imageResp.Images) > 1 && s.AmiFilters.MostRecent == false {
		err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	var image *ec2.Image
	if s.AmiFilters.MostRecent {
		image = mostRecentAmi(imageResp.Images)
	} else {
		image = imageResp.Images[0]
	}

	ui.Message(fmt.Sprintf("Found Image ID: %s", *image.ImageId))

	// Enhanced Networking (SriovNetSupport) can only be enabled on HVM AMIs.
	// See http://goo.gl/icuXh5
	if s.EnhancedNetworking && *image.VirtualizationType != "hvm" {
		err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("source_image", image)
	return multistep.ActionContinue
}

func (s *StepSourceAMIInfo) Cleanup(multistep.StateBag) {}
<commit_msg>amazon: Fix source_ami_filter ignores owners<commit_after>package common

import (
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

// StepSourceAMIInfo extracts critical information from the source AMI
// that is used throughout the AMI creation process.
//
// Produces:
//   source_image *ec2.Image - the source AMI info
type StepSourceAMIInfo struct {
	SourceAmi          string
	EnhancedNetworking bool
	AmiFilters         AmiFilterOptions
}

// Build a slice of AMI filter options from the filters provided.
func buildAmiFilters(input map[*string]*string) []*ec2.Filter {
	var filters []*ec2.Filter
	for k, v := range input {
		filters = append(filters, &ec2.Filter{
			Name:   k,
			Values: []*string{v},
		})
	}
	return filters
}

type imageSort []*ec2.Image

func (a imageSort) Len() int      { return len(a) }
func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a imageSort) Less(i, j int) bool {
	itime, _ := time.Parse(time.RFC3339, *a[i].CreationDate)
	jtime, _ := time.Parse(time.RFC3339, *a[j].CreationDate)
	return itime.Unix() < jtime.Unix()
}

// Returns the most recent AMI out of a slice of images.
func mostRecentAmi(images []*ec2.Image) *ec2.Image {
	sortedImages := images
	sort.Sort(imageSort(sortedImages))
	return sortedImages[len(sortedImages)-1]
}

func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
	ec2conn := state.Get("ec2").(*ec2.EC2)
	ui := state.Get("ui").(packer.Ui)

	params := &ec2.DescribeImagesInput{}

	if s.SourceAmi != "" {
		params.ImageIds = []*string{&s.SourceAmi}
	}

	// We have filters to apply
	if len(s.AmiFilters.Filters) > 0 {
		params.Filters = buildAmiFilters(s.AmiFilters.Filters)
	}
	if len(s.AmiFilters.Owners) > 0 {
		params.Owners = s.AmiFilters.Owners
	}

	log.Printf("Using AMI Filters %v", params)
	imageResp, err := ec2conn.DescribeImages(params)
	if err != nil {
		err := fmt.Errorf("Error querying AMI: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if len(imageResp.Images) == 0 {
		err := fmt.Errorf("No AMI was found matching filters: %v", params)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if len(imageResp.Images) > 1 && s.AmiFilters.MostRecent == false {
		err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	var image *ec2.Image
	if s.AmiFilters.MostRecent {
		image = mostRecentAmi(imageResp.Images)
	} else {
		image = imageResp.Images[0]
	}

	ui.Message(fmt.Sprintf("Found Image ID: %s", *image.ImageId))

	// Enhanced Networking (SriovNetSupport) can only be enabled on HVM AMIs.
	// See http://goo.gl/icuXh5
	if s.EnhancedNetworking && *image.VirtualizationType != "hvm" {
		err := fmt.Errorf("Cannot enable enhanced networking, source AMI '%s' is not HVM", s.SourceAmi)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("source_image", image)
	return multistep.ActionContinue
}

func (s *StepSourceAMIInfo) Cleanup(multistep.StateBag) {}
<|endoftext|>


<commit_before>package gokeepasslib

import "encoding/base64"

var iv = []byte{0xe8, 0x30, 0x09, 0x4b, 0x97, 0x20, 0x5d, 0x2a}
var sigmaWords = []uint32{
	0x61707865,
	0x3320646e,
	0x79622d32,
	0x6b206574,
}

// SalsaManager is responsible for stream encrypting and decrypting of the passwords
type SalsaManager struct {
	State        []uint32
	blockUsed    int
	block        []byte
	counterWords [2]int
	currentBlock []byte
}

func (s *SalsaManager) UnlockGroups(gs []Group) {
	for i, _ := range gs { //For each top level group
		s.UnlockGroup(&gs[i])
	}
}
func (s *SalsaManager) UnlockGroup(g *Group) {
	s.UnlockEntries(g.Entries)
	s.UnlockGroups(g.Groups)
}
func (s *SalsaManager) UnlockEntries(e []Entry) {
	for i, _ := range e {
		s.UnlockEntry(&e[i])
	}
}
func (s *SalsaManager) UnlockEntry(e *Entry) {
	for i, _ := range e.Values {
		if bool(e.Values[i].Value.Protected) {
			e.Values[i].Value.Content = string(s.Unpack(e.Values[i].Value.Content))
		}
	}
	for i, _ := range e.Histories {
		s.UnlockEntries(e.Histories[i].Entries)
	}
}

func (s *SalsaManager) LockGroups(gs []Group) {
	for i, _ := range gs {
		s.LockGroup(&gs[i])
	}
}
func (s *SalsaManager) LockGroup(g *Group) {
	s.LockEntries(g.Entries)
	s.LockGroups(g.Groups)
}
func (s *SalsaManager) LockEntries(es []Entry) {
	for i, _ := range es {
		s.LockEntry(&es[i])
	}
}
func (s *SalsaManager) LockEntry(e *Entry) {
	for i, _ := range e.Values {
		if bool(e.Values[i].Value.Protected) {
			e.Values[i].Value.Content = s.Pack([]byte(e.Values[i].Value.Content))
		}
	}
	for i, _ := range e.Histories {
		s.UnlockEntries(e.Histories[i].Entries)
	}
}

func u8to32little(k []byte, i int) uint32 {
	return uint32(k[i]) |
		(uint32(k[i+1]) << 8) |
		(uint32(k[i+2]) << 16) |
		(uint32(k[i+3]) << 24)
}

func rotl32(x uint32, b uint) uint32 {
	return ((x << b) | (x >> (32 - b)))
}

// NewSalsaManager initializes a new Password
func NewSalsaManager(key [32]byte) *SalsaManager {
	state := make([]uint32, 16)

	state[1] = u8to32little(key[:], 0)
	state[2] = u8to32little(key[:], 4)
	state[3] = u8to32little(key[:], 8)
	state[4] = u8to32little(key[:], 12)
	state[11] = u8to32little(key[:], 16)
	state[12] = u8to32little(key[:], 20)
	state[13] = u8to32little(key[:], 24)
	state[14] = u8to32little(key[:], 28)
	state[0] = sigmaWords[0]
	state[5] = sigmaWords[1]
	state[10] = sigmaWords[2]
	state[15] = sigmaWords[3]

	state[6] = u8to32little(iv, 0)
	state[7] = u8to32little(iv, 4)
	state[8] = uint32(0)
	state[9] = uint32(0)

	s := SalsaManager{
		State:        state,
		currentBlock: make([]byte, 0),
	}
	s.reset()
	return &s
}

func (s *SalsaManager) Unpack(payload string) []byte {
	var result []byte

	data, _ := base64.StdEncoding.DecodeString(payload)

	salsaBytes := s.fetchBytes(len(data))

	for i := 0; i < len(data); i++ {
		result = append(result, salsaBytes[i]^data[i])
	}

	return result
}

func (s *SalsaManager) Pack(payload []byte) string {
	var data []byte

	salsaBytes := s.fetchBytes(len(payload))

	for i := 0; i < len(payload); i++ {
		data = append(data, salsaBytes[i]^payload[i])
	}

	lockedPassword := base64.StdEncoding.EncodeToString(data)
	return lockedPassword
}

func (s *SalsaManager) reset() {
	s.blockUsed = 64
	s.counterWords = [2]int{0, 0}
}

func (s *SalsaManager) incrementCounter() {
	s.counterWords[0] = (s.counterWords[0] + 1) & 0xffffffff
	if s.counterWords[0] == 0 {
		s.counterWords[1] = (s.counterWords[1] + 1) & 0xffffffff
	}
}

func (s *SalsaManager) fetchBytes(length int) []byte {
	for length > len(s.currentBlock) {
		s.currentBlock = append(s.currentBlock, s.getBytes(64)...)
	}

	data := s.currentBlock[0:length]
	s.currentBlock = s.currentBlock[length:]

	return data
}

func (s *SalsaManager) getBytes(length int) []byte {
	b := make([]byte, length)

	for i := 0; i < length; i++ {
		if s.blockUsed == 64 {
			s.generateBlock()
			s.incrementCounter()
			s.blockUsed = 0
		}
		b[i] = s.block[s.blockUsed]
		s.blockUsed++
	}

	return b
}

func (s *SalsaManager) generateBlock() {
	s.block = make([]byte, 64)

	x := make([]uint32, 16)
	copy(x, s.State)

	for i := 0; i < 10; i++ {
		x[4] = x[4] ^ rotl32(x[0]+x[12], 7)
		x[8] = x[8] ^ rotl32(x[4]+x[0], 9)
		x[12] = x[12] ^ rotl32(x[8]+x[4], 13)
		x[0] = x[0] ^ rotl32(x[12]+x[8], 18)

		x[9] = x[9] ^ rotl32(x[5]+x[1], 7)
		x[13] = x[13] ^ rotl32(x[9]+x[5], 9)
		x[1] = x[1] ^ rotl32(x[13]+x[9], 13)
		x[5] = x[5] ^ rotl32(x[1]+x[13], 18)

		x[14] = x[14] ^ rotl32(x[10]+x[6], 7)
		x[2] = x[2] ^ rotl32(x[14]+x[10], 9)
		x[6] = x[6] ^ rotl32(x[2]+x[14], 13)
		x[10] = x[10] ^ rotl32(x[6]+x[2], 18)

		x[3] = x[3] ^ rotl32(x[15]+x[11], 7)
		x[7] = x[7] ^ rotl32(x[3]+x[15], 9)
		x[11] = x[11] ^ rotl32(x[7]+x[3], 13)
		x[15] = x[15] ^ rotl32(x[11]+x[7], 18)

		x[1] = x[1] ^ rotl32(x[0]+x[3], 7)
		x[2] = x[2] ^ rotl32(x[1]+x[0], 9)
		x[3] = x[3] ^ rotl32(x[2]+x[1], 13)
		x[0] = x[0] ^ rotl32(x[3]+x[2], 18)

		x[6] = x[6] ^ rotl32(x[5]+x[4], 7)
		x[7] = x[7] ^ rotl32(x[6]+x[5], 9)
		x[4] = x[4] ^ rotl32(x[7]+x[6], 13)
		x[5] = x[5] ^ rotl32(x[4]+x[7], 18)

		x[11] = x[11] ^ rotl32(x[10]+x[9], 7)
		x[8] = x[8] ^ rotl32(x[11]+x[10], 9)
		x[9] = x[9] ^ rotl32(x[8]+x[11], 13)
		x[10] = x[10] ^ rotl32(x[9]+x[8], 18)

		x[12] = x[12] ^ rotl32(x[15]+x[14], 7)
		x[13] = x[13] ^ rotl32(x[12]+x[15], 9)
		x[14] = x[14] ^ rotl32(x[13]+x[12], 13)
		x[15] = x[15] ^ rotl32(x[14]+x[13], 18)
	}

	for i := 0; i < 16; i++ {
		x[i] += s.State[i]
	}

	for i := 0; i < 16; i++ {
		s.block[i<<2] = byte(x[i])
		s.block[(i<<2)+1] = byte(x[i] >> 8)
		s.block[(i<<2)+2] = byte(x[i] >> 16)
		s.block[(i<<2)+3] = byte(x[i] >> 24)
	}
	s.blockUsed = 0
	s.State[8]++
	if s.State[8] == 0 {
		s.State[9]++
	}
}
<commit_msg>fix building with go1.8<commit_after>package gokeepasslib

import "encoding/base64"

var iv = []byte{0xe8, 0x30, 0x09, 0x4b, 0x97, 0x20, 0x5d, 0x2a}
var sigmaWords = []uint32{
	0x61707865,
	0x3320646e,
	0x79622d32,
	0x6b206574,
}

// SalsaManager is responsible for stream encrypting and decrypting of the passwords
type SalsaManager struct {
	State        []uint32
	blockUsed    int
	block        []byte
	counterWords [2]uint32
	currentBlock []byte
}

func (s *SalsaManager) UnlockGroups(gs []Group) {
	for i, _ := range gs { //For each top level group
		s.UnlockGroup(&gs[i])
	}
}
func (s *SalsaManager) UnlockGroup(g *Group) {
	s.UnlockEntries(g.Entries)
	s.UnlockGroups(g.Groups)
}
func (s *SalsaManager) UnlockEntries(e []Entry) {
	for i, _ := range e {
		s.UnlockEntry(&e[i])
	}
}
func (s *SalsaManager) UnlockEntry(e *Entry) {
	for i, _ := range e.Values {
		if bool(e.Values[i].Value.Protected) {
			e.Values[i].Value.Content = string(s.Unpack(e.Values[i].Value.Content))
		}
	}
	for i, _ := range e.Histories {
		s.UnlockEntries(e.Histories[i].Entries)
	}
}

func (s *SalsaManager) LockGroups(gs []Group) {
	for i, _ := range gs {
		s.LockGroup(&gs[i])
	}
}
func (s *SalsaManager) LockGroup(g *Group) {
	s.LockEntries(g.Entries)
	s.LockGroups(g.Groups)
}
func (s *SalsaManager) LockEntries(es []Entry) {
	for i, _ := range es {
		s.LockEntry(&es[i])
	}
}
func (s *SalsaManager) LockEntry(e *Entry) {
	for i, _ := range e.Values {
		if bool(e.Values[i].Value.Protected) {
			e.Values[i].Value.Content = s.Pack([]byte(e.Values[i].Value.Content))
		}
	}
	for i, _ := range e.Histories {
		s.UnlockEntries(e.Histories[i].Entries)
	}
}

func u8to32little(k []byte, i int) uint32 {
	return uint32(k[i]) |
		(uint32(k[i+1]) << 8) |
		(uint32(k[i+2]) << 16) |
		(uint32(k[i+3]) << 24)
}

func rotl32(x uint32, b uint) uint32 {
	return ((x << b) | (x >> (32 - b)))
}

// NewSalsaManager initializes a new Password
func NewSalsaManager(key [32]byte) *SalsaManager {
	state := make([]uint32, 16)

	state[1] = u8to32little(key[:], 0)
	state[2] = u8to32little(key[:], 4)
	state[3] = u8to32little(key[:], 8)
	state[4] = u8to32little(key[:], 12)
	state[11] = u8to32little(key[:], 16)
	state[12] = u8to32little(key[:], 20)
	state[13] = u8to32little(key[:], 24)
	state[14] = u8to32little(key[:], 28)
	state[0] = sigmaWords[0]
	state[5] = sigmaWords[1]
	state[10] = sigmaWords[2]
	state[15] = sigmaWords[3]

	state[6] = u8to32little(iv, 0)
	state[7] = u8to32little(iv, 4)
	state[8] = uint32(0)
	state[9] = uint32(0)

	s := SalsaManager{
		State:        state,
		currentBlock: make([]byte, 0),
	}
	s.reset()
	return &s
}

func (s *SalsaManager) Unpack(payload string) []byte {
	var result []byte

	data, _ := base64.StdEncoding.DecodeString(payload)

	salsaBytes := s.fetchBytes(len(data))

	for i := 0; i < len(data); i++ {
		result = append(result, salsaBytes[i]^data[i])
	}

	return result
}

func (s *SalsaManager) Pack(payload []byte) string {
	var data []byte

	salsaBytes := s.fetchBytes(len(payload))

	for i := 0; i < len(payload); i++ {
		data = append(data, salsaBytes[i]^payload[i])
	}

	lockedPassword := base64.StdEncoding.EncodeToString(data)
	return lockedPassword
}

func (s *SalsaManager) reset() {
	s.blockUsed = 64
	s.counterWords = [2]uint32{0, 0}
}

func (s *SalsaManager) incrementCounter() {
	s.counterWords[0] = (s.counterWords[0] + 1) & 0xffffffff
	if s.counterWords[0] == 0 {
		s.counterWords[1] = (s.counterWords[1] + 1) & 0xffffffff
	}
}

func (s *SalsaManager) fetchBytes(length int) []byte {
	for length > len(s.currentBlock) {
		s.currentBlock = append(s.currentBlock, s.getBytes(64)...)
	}

	data := s.currentBlock[0:length]
	s.currentBlock = s.currentBlock[length:]

	return data
}

func (s *SalsaManager) getBytes(length int) []byte {
	b := make([]byte, length)

	for i := 0; i < length; i++ {
		if s.blockUsed == 64 {
			s.generateBlock()
			s.incrementCounter()
			s.blockUsed = 0
		}
		b[i] = s.block[s.blockUsed]
		s.blockUsed++
	}

	return b
}

func (s *SalsaManager) generateBlock() {
	s.block = make([]byte, 64)

	x := make([]uint32, 16)
	copy(x, s.State)

	for i := 0; i < 10; i++ {
		x[4] = x[4] ^ rotl32(x[0]+x[12], 7)
		x[8] = x[8] ^ rotl32(x[4]+x[0], 9)
		x[12] = x[12] ^ rotl32(x[8]+x[4], 13)
		x[0] = x[0] ^ rotl32(x[12]+x[8], 18)

		x[9] = x[9] ^ rotl32(x[5]+x[1], 7)
		x[13] = x[13] ^ rotl32(x[9]+x[5], 9)
		x[1] = x[1] ^ rotl32(x[13]+x[9], 13)
		x[5] = x[5] ^ rotl32(x[1]+x[13], 18)

		x[14] = x[14] ^ rotl32(x[10]+x[6], 7)
		x[2] = x[2] ^ rotl32(x[14]+x[10], 9)
		x[6] = x[6] ^ rotl32(x[2]+x[14], 13)
		x[10] = x[10] ^ rotl32(x[6]+x[2], 18)

		x[3] = x[3] ^ rotl32(x[15]+x[11], 7)
		x[7] = x[7] ^ rotl32(x[3]+x[15], 9)
		x[11] = x[11] ^ rotl32(x[7]+x[3], 13)
		x[15] = x[15] ^ rotl32(x[11]+x[7], 18)

		x[1] = x[1] ^ rotl32(x[0]+x[3], 7)
		x[2] = x[2] ^ rotl32(x[1]+x[0], 9)
		x[3] = x[3] ^ rotl32(x[2]+x[1], 13)
		x[0] = x[0] ^ rotl32(x[3]+x[2], 18)

		x[6] = x[6] ^ rotl32(x[5]+x[4], 7)
		x[7] = x[7] ^ rotl32(x[6]+x[5], 9)
		x[4] = x[4] ^ rotl32(x[7]+x[6], 13)
		x[5] = x[5] ^ rotl32(x[4]+x[7], 18)

		x[11] = x[11] ^ rotl32(x[10]+x[9], 7)
		x[8] = x[8] ^ rotl32(x[11]+x[10], 9)
		x[9] = x[9] ^ rotl32(x[8]+x[11], 13)
		x[10] = x[10] ^ rotl32(x[9]+x[8], 18)

		x[12] = x[12] ^ rotl32(x[15]+x[14], 7)
		x[13] = x[13] ^ rotl32(x[12]+x[15], 9)
		x[14] = x[14] ^ rotl32(x[13]+x[12], 13)
		x[15] = x[15] ^ rotl32(x[14]+x[13], 18)
	}

	for i := 0; i < 16; i++ {
		x[i] += s.State[i]
	}

	for i := 0; i < 16; i++ {
		s.block[i<<2] = byte(x[i])
		s.block[(i<<2)+1] = byte(x[i] >> 8)
		s.block[(i<<2)+2] = byte(x[i] >> 16)
		s.block[(i<<2)+3] = byte(x[i] >> 24)
	}
	s.blockUsed = 0
	s.State[8]++
	if s.State[8] == 0 {
		s.State[9]++
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"os"
	"strconv"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil/hdkeychain"

	"moonchan/channels"
)

type Channel struct {
	Domain   string
	Host     string
	KeyPath  int
	RemoteID string

	PendingPayment []byte

	State channels.SharedState
}

type State struct {
	Seed           []byte
	XPrivKey       string
	KeyPathCounter int
	Channels       map[string]Channel
}

func (s *State) NextKey() int {
	c := s.KeyPathCounter
	s.KeyPathCounter++
	return c
}

func newState(net *chaincfg.Params) (*State, error) {
	seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
	if err != nil {
		return nil, err
	}
	key, err := hdkeychain.NewMaster(seed, net)
	if err != nil {
		return nil, err
	}

	return &State{
		Seed:     seed,
		XPrivKey: key.String(),
		Channels: make(map[string]Channel),
	}, nil
}

func getFilename(net *chaincfg.Params) string {
	if net == &chaincfg.TestNet3Params {
		return "client-state.testnet3.json"
	}
	return "client-state.mainnet.json"
}

func save(net *chaincfg.Params, s *State) error {
	suffix := strconv.FormatInt(time.Now().Unix(), 10)

	name := getFilename(net)
	tmpName := name + ".tmp." + suffix

	f, err := os.Create(tmpName)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := json.NewEncoder(f).Encode(s); err != nil {
		return err
	}

	if err := f.Close(); err != nil {
		return err
	}

	return os.Rename(tmpName, name)
}

func createNew(net *chaincfg.Params) (*State, error) {
	s, err := newState(net)
	if err != nil {
		return nil, err
	}
	if err := save(net, s); err != nil {
		return nil, err
	}
	return s, nil
}

func load(net *chaincfg.Params) (*State, error) {
	name := getFilename(net)
	f, err := os.Open(name)
	if os.IsNotExist(err) {
		return createNew(net)
	} else if err != nil {
		return nil, err
	}
	defer f.Close()

	var s State
	if err := json.NewDecoder(f).Decode(&s); err != nil {
		return nil, err
	}

	return &s, nil
}

var globalState *State

func getChannel(id string) (*Channel, *channels.Sender, error) {
	s, ok := globalState.Channels[id]
	if !ok {
		return nil, nil, errors.New("unknown id")
	}

	privkey, _, err := loadkey(globalState, s.KeyPath)
	if err != nil {
		return nil, nil, err
	}

	sender, err := channels.NewSender(s.State, privkey)
	if err != nil {
		return nil, nil, err
	}

	return &s, sender, nil
}

func storeChannel(id string, state channels.SharedState) error {
	c, ok := globalState.Channels[id]
	if !ok {
		return errors.New("channel does not exist")
	}
	c.State = state
	globalState.Channels[id] = c
	return nil
}

func storePendingPayment(id string, state channels.SharedState, p []byte) error {
	c, ok := globalState.Channels[id]
	if !ok {
		return errors.New("channel does not exist")
	}
	c.State = state
	c.PendingPayment = p
	globalState.Channels[id] = c
	return nil
}

func findForDomain(domain string) []string {
	var ids []string
	for id, c := range globalState.Channels {
		if c.Domain == domain && c.State.Status == channels.StatusOpen {
			ids = append(ids, id)
		}
	}
	return ids
}

func hasRemoteID(domain, remoteID string) bool {
	for _, c := range globalState.Channels {
		if c.Domain == domain && c.RemoteID == remoteID {
			return true
		}
	}
	return false
}
<commit_msg>keep list of payments on client<commit_after>package main

import (
	"encoding/json"
	"errors"
	"os"
	"strconv"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil/hdkeychain"

	"moonchan/channels"
)

type Channel struct {
	Domain   string
	Host     string
	KeyPath  int
	RemoteID string

	PendingPayment []byte

	State channels.SharedState

	Payments [][]byte
}

type State struct {
	Seed           []byte
	XPrivKey       string
	KeyPathCounter int
	Channels       map[string]Channel
}

func (s *State) NextKey() int {
	c := s.KeyPathCounter
	s.KeyPathCounter++
	return c
}

func newState(net *chaincfg.Params) (*State, error) {
	seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
	if err != nil {
		return nil, err
	}
	key, err := hdkeychain.NewMaster(seed, net)
	if err != nil {
		return nil, err
	}

	return &State{
		Seed:     seed,
		XPrivKey: key.String(),
		Channels: make(map[string]Channel),
	}, nil
}

func getFilename(net *chaincfg.Params) string {
	if net == &chaincfg.TestNet3Params {
		return "client-state.testnet3.json"
	}
	return "client-state.mainnet.json"
}

func save(net *chaincfg.Params, s *State) error {
	suffix := strconv.FormatInt(time.Now().Unix(), 10)

	name := getFilename(net)
	tmpName := name + ".tmp." + suffix

	f, err := os.Create(tmpName)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := json.NewEncoder(f).Encode(s); err != nil {
		return err
	}

	if err := f.Close(); err != nil {
		return err
	}

	return os.Rename(tmpName, name)
}

func createNew(net *chaincfg.Params) (*State, error) {
	s, err := newState(net)
	if err != nil {
		return nil, err
	}
	if err := save(net, s); err != nil {
		return nil, err
	}
	return s, nil
}

func load(net *chaincfg.Params) (*State, error) {
	name := getFilename(net)
	f, err := os.Open(name)
	if os.IsNotExist(err) {
		return createNew(net)
	} else if err != nil {
		return nil, err
	}
	defer f.Close()

	var s State
	if err := json.NewDecoder(f).Decode(&s); err != nil {
		return nil, err
	}

	return &s, nil
}

var globalState *State

func getChannel(id string) (*Channel, *channels.Sender, error) {
	s, ok := globalState.Channels[id]
	if !ok {
		return nil, nil, errors.New("unknown id")
	}

	privkey, _, err := loadkey(globalState, s.KeyPath)
	if err != nil {
		return nil, nil, err
	}

	sender, err := channels.NewSender(s.State, privkey)
	if err != nil {
		return nil, nil, err
	}

	return &s, sender, nil
}

func storeChannel(id string, state channels.SharedState) error {
	c, ok := globalState.Channels[id]
	if !ok {
		return errors.New("channel does not exist")
	}
	c.State = state
	globalState.Channels[id] = c
	return nil
}

func storePendingPayment(id string, state channels.SharedState, p []byte) error {
	c, ok := globalState.Channels[id]
	if !ok {
		return errors.New("channel does not exist")
	}
	c.State = state
	if p == nil {
		c.Payments = append(c.Payments, c.PendingPayment)
		c.PendingPayment = nil
	} else {
		c.PendingPayment = p
	}
	globalState.Channels[id] = c
	return nil
}

func findForDomain(domain string) []string {
	var ids []string
	for id, c := range globalState.Channels {
		if c.Domain == domain && c.State.Status == channels.StatusOpen {
			ids = append(ids, id)
		}
	}
	return ids
}

func hasRemoteID(domain, remoteID string) bool {
	for _, c := range globalState.Channels {
		if c.Domain == domain && c.RemoteID == remoteID {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Steven Labrum

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 CheckIt

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

var outputs = [10]*CompileOut{}
var boxes = []*BoxStruct{}
var interfaces = []Box{}
var page = Page{}
var aboutPage = Page{}
var about = AboutStruct{}
var configuration = &Config{}

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

func baseCase(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, page)

	for key := range boxes {
		boxTemp.Execute(w, boxes[key])
	}

	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

/*  FrontPage is an HTTP handler that displays the basecase
unless a stored page is being loaded.
*/
func FrontPage(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/"):]

	if len(title) < 1 {
		baseCase(w, r)
	} else {
		title := r.URL.Path[len("/"):]
		title = configuration.Path + "/" + title
		fmt.Println(title)
		pageNames, _ := filepath.Glob(title + "/*.page")
		boxNames, _ := filepath.Glob(title + "/*.box")

		fmt.Println(len(pageNames))
		fmt.Println("Loaded shared page")

		if pageNames == nil || boxNames == nil {
			http.Redirect(w, r, "/", http.StatusFound)
		} else {

			pageName := pageNames[0]
			fmt.Println(boxNames)

			headTemp.Execute(w, nil)
			openBodyTemp.Execute(w, nil)

			configuration = ReadConfig(configuration.Path + pageName)

			initConfig(configuration)

			pageStartTemp.Execute(w, page)

			boxes = []*BoxStruct{}
			for key := range boxNames {
				boxP := ReadBox(configuration.Path + boxNames[key])
				boxes = append(boxes, boxP)
				boxTemp.Execute(w, boxP)
			}

			pageCloseTemp.Execute(w, nil)
			htmlCloseTemp.Execute(w, nil)
		}
	}
}

func AboutPage(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, aboutPage)
	aboutTemp.Execute(w, about)
	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

var outputText = `<pre>{{printf "%s" . |html}}</pre>`
var output = template.Must(template.New("output").Parse(outputText))
var shareText = `{{printf "%s" . |html}}`
var shareOutput = template.Must(template.New("shareOutput").Parse(shareText))

// Compile is an HTTP handler that reads Source code from the request,
// runs the program (returning any errors),
// and sends the program's output as the HTTP response.
func PipeCompile(w http.ResponseWriter, req *http.Request) {

	title := req.URL.Path[len("/pipeile/"):]

	fmt.Println(title)
	str := strings.Split(title, "/")
	title = str[0]

	position, _ := strconv.Atoi(str[1])

	body := new(bytes.Buffer)

	if _, err := body.ReadFrom(req.Body); err != nil {
		return
	}

	fmt.Println(position)

	updateBody(boxes, title, body.String())

	out, err := InterfaceRun(interfaces[position-1], body.Bytes(), title)
	compOut := CompileOut{Out: out, Error: err}

	outputs[position-1] = &compOut

	if err != nil {
		w.WriteHeader(404)
		output.Execute(w, out)
	} else if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

func sharHandler(w http.ResponseWriter, r *http.Request) {
	out := Share()
	fmt.Println("PATH :/ " + configuration.Path)
	Save(configuration.Path+"/", out)
	shareOutput.Execute(w, out)
}

func initConfig(config *Config) {
	configuration = config
	page.Heading = config.Heading
	page.SubHeading = config.SubHeading
	about.Text = config.About
	about.SecondaryText = config.AboutSide
	aboutPage.Heading = "About"
	aboutPage.SubHeading = ""
}

func initBoxes(boxs ...Box) {

	for key := range boxs {

		var box = BoxStruct{}

		box.Id = strconv.Itoa(key)
		box.Position = strconv.Itoa(key + 1)
		box.Total = len(boxs)
		box.Lang = boxs[key].Syntax()
		box.Body = boxs[key].Default()
		box.Head = "Heading"
		box.SubHead = "Subhead"
		box.Text = boxs[key].Help()

		boxes = append(boxes, &box)

		interfaces = append(interfaces, boxs[key])

	}
}

func Serve(config *Config, boxs ...Box) (err error) {
	initConfig(config)
	initBoxes(boxs...)

	fmt.Println("cool beans")
	http.HandleFunc("/share/", sharHandler)
	http.HandleFunc("/about", AboutPage)
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile/", PipeCompile)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/fonts/", http.StripPrefix("/fonts/", http.FileServer(http.Dir("fonts"))))
	http.Handle("/js/", http.StripPrefix("/js", http.FileServer(http.Dir("js"))))
	http.ListenAndServe(":"+configuration.Port, nil)

	err = errors.New("Server crashed")
	return err
}
<commit_msg>.page -> .config<commit_after>/*
Copyright 2015 Steven Labrum

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 CheckIt

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

var outputs = [10]*CompileOut{}
var boxes = []*BoxStruct{}
var interfaces = []Box{}
var page = Page{}
var aboutPage = Page{}
var about = AboutStruct{}
var configuration = &Config{}

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

func baseCase(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, page)

	for key := range boxes {
		boxTemp.Execute(w, boxes[key])
	}

	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

/*  FrontPage is an HTTP handler that displays the basecase
unless a stored page is being loaded.
*/
func FrontPage(w http.ResponseWriter, r *http.Request) {
	title := r.URL.Path[len("/"):]

	if len(title) < 1 {
		baseCase(w, r)
	} else {
		title := r.URL.Path[len("/"):]
		title = configuration.Path + "/" + title
		fmt.Println(title)
		pageNames, _ := filepath.Glob(title + "/*.config")
		boxNames, _ := filepath.Glob(title + "/*.box")

		fmt.Println(len(pageNames))
		fmt.Println("Loaded shared page")

		if pageNames == nil || boxNames == nil {
			http.Redirect(w, r, "/", http.StatusFound)
		} else {

			pageName := pageNames[0]
			fmt.Println(boxNames)

			headTemp.Execute(w, nil)
			openBodyTemp.Execute(w, nil)

			configuration = ReadConfig(configuration.Path + pageName)

			initConfig(configuration)

			pageStartTemp.Execute(w, page)

			boxes = []*BoxStruct{}
			for key := range boxNames {
				boxP := ReadBox(configuration.Path + boxNames[key])
				boxes = append(boxes, boxP)
				boxTemp.Execute(w, boxP)
			}

			pageCloseTemp.Execute(w, nil)
			htmlCloseTemp.Execute(w, nil)
		}
	}
}

func AboutPage(w http.ResponseWriter, r *http.Request) {

	headTemp.Execute(w, nil)
	openBodyTemp.Execute(w, nil)
	pageStartTemp.Execute(w, aboutPage)
	aboutTemp.Execute(w, about)
	pageCloseTemp.Execute(w, nil)
	htmlCloseTemp.Execute(w, nil)

}

var outputText = `<pre>{{printf "%s" . |html}}</pre>`
var output = template.Must(template.New("output").Parse(outputText))
var shareText = `{{printf "%s" . |html}}`
var shareOutput = template.Must(template.New("shareOutput").Parse(shareText))

// Compile is an HTTP handler that reads Source code from the request,
// runs the program (returning any errors),
// and sends the program's output as the HTTP response.
func PipeCompile(w http.ResponseWriter, req *http.Request) {

	title := req.URL.Path[len("/pipeile/"):]

	fmt.Println(title)
	str := strings.Split(title, "/")
	title = str[0]

	position, _ := strconv.Atoi(str[1])

	body := new(bytes.Buffer)

	if _, err := body.ReadFrom(req.Body); err != nil {
		return
	}

	fmt.Println(position)

	updateBody(boxes, title, body.String())

	out, err := InterfaceRun(interfaces[position-1], body.Bytes(), title)
	compOut := CompileOut{Out: out, Error: err}

	outputs[position-1] = &compOut

	if err != nil {
		w.WriteHeader(404)
		output.Execute(w, out)
	} else if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

func sharHandler(w http.ResponseWriter, r *http.Request) {
	out := Share()
	fmt.Println("PATH :/ " + configuration.Path)
	Save(configuration.Path+"/", out)
	shareOutput.Execute(w, out)
}

func initConfig(config *Config) {
	configuration = config
	page.Heading = config.Heading
	page.SubHeading = config.SubHeading
	about.Text = config.About
	about.SecondaryText = config.AboutSide
	aboutPage.Heading = "About"
	aboutPage.SubHeading = ""
}

func initBoxes(boxs ...Box) {

	for key := range boxs {

		var box = BoxStruct{}

		box.Id = strconv.Itoa(key)
		box.Position = strconv.Itoa(key + 1)
		box.Total = len(boxs)
		box.Lang = boxs[key].Syntax()
		box.Body = boxs[key].Default()
		box.Head = "Heading"
		box.SubHead = "Subhead"
		box.Text = boxs[key].Help()

		boxes = append(boxes, &box)

		interfaces = append(interfaces, boxs[key])

	}
}

func Serve(config *Config, boxs ...Box) (err error) {
	initConfig(config)
	initBoxes(boxs...)

	fmt.Println("cool beans")
	http.HandleFunc("/share/", sharHandler)
	http.HandleFunc("/about", AboutPage)
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile/", PipeCompile)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/fonts/", http.StripPrefix("/fonts/", http.FileServer(http.Dir("fonts"))))
	http.Handle("/js/", http.StripPrefix("/js", http.FileServer(http.Dir("js"))))
	http.ListenAndServe(":"+configuration.Port, nil)

	err = errors.New("Server crashed")
	return err
}
<|endoftext|>


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

package gzip

import (
	"io"
	"io/ioutil"
	"strings"
	"testing"
)

// Tests that gzipping and then gunzipping is the identity function.
func TestWriter(t *testing.T) {
	// Set up the Pipe to do the gzip and gunzip.
	piper, pipew := io.Pipe()
	defer piper.Close()
	go func() {
		defer pipew.Close()
		deflater, err := NewDeflater(pipew)
		if err != nil {
			t.Errorf("%v", err)
			return
		}
		defer deflater.Close()
		deflater.Comment = "comment"
		deflater.Extra = strings.Bytes("extra")
		deflater.Mtime = 1e8
		deflater.Name = "name"
		_, err = deflater.Write(strings.Bytes("payload"))
		if err != nil {
			t.Errorf("%v", err)
			return
		}
	}()
	inflater, err := NewInflater(piper)
	if err != nil {
		t.Errorf("%v", err)
		return
	}
	defer inflater.Close()

	// Read and compare to the original input.
	b, err := ioutil.ReadAll(inflater)
	if err != nil {
		t.Errorf(": %v", err)
		return
	}
	if string(b) != "payload" {
		t.Fatalf("payload is %q, want %q", string(b), "payload")
	}
	if inflater.Comment != "comment" {
		t.Fatalf("comment is %q, want %q", inflater.Comment, "comment")
	}
	if string(inflater.Extra) != "extra" {
		t.Fatalf("extra is %q, want %q", inflater.Extra, "extra")
	}
	if inflater.Mtime != 1e8 {
		t.Fatalf("mtime is %d, want %d", inflater.Mtime, uint32(1e8))
	}
	if inflater.Name != "name" {
		t.Fatalf("name is %q, want %q", inflater.Name, "name")
	}
}
<commit_msg>Add a GZIP test for the empty payload.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gzip

import (
	"io"
	"io/ioutil"
	"strings"
	"testing"
)

// pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the
// writer end and ifunc at the reader end.
func pipe(t *testing.T, dfunc func(*Deflater), ifunc func(*Inflater)) {
	piper, pipew := io.Pipe()
	defer piper.Close()
	go func() {
		defer pipew.Close()
		deflater, err := NewDeflater(pipew)
		if err != nil {
			t.Fatalf("%v", err)
		}
		defer deflater.Close()
		dfunc(deflater)
	}()
	inflater, err := NewInflater(piper)
	if err != nil {
		t.Fatalf("%v", err)
	}
	defer inflater.Close()
	ifunc(inflater)
}

// Tests that an empty payload still forms a valid GZIP stream.
func TestEmpty(t *testing.T) {
	pipe(t,
		func(deflater *Deflater) {},
		func(inflater *Inflater) {
			b, err := ioutil.ReadAll(inflater)
			if err != nil {
				t.Fatalf("%v", err)
			}
			if len(b) != 0 {
				t.Fatalf("did not read an empty slice")
			}
		})
}

// Tests that gzipping and then gunzipping is the identity function.
func TestWriter(t *testing.T) {
	pipe(t,
		func(deflater *Deflater) {
			deflater.Comment = "comment"
			deflater.Extra = strings.Bytes("extra")
			deflater.Mtime = 1e8
			deflater.Name = "name"
			_, err := deflater.Write(strings.Bytes("payload"))
			if err != nil {
				t.Fatalf("%v", err)
			}
		},
		func(inflater *Inflater) {
			b, err := ioutil.ReadAll(inflater)
			if err != nil {
				t.Fatalf("%v", err)
			}
			if string(b) != "payload" {
				t.Fatalf("payload is %q, want %q", string(b), "payload")
			}
			if inflater.Comment != "comment" {
				t.Fatalf("comment is %q, want %q", inflater.Comment, "comment")
			}
			if string(inflater.Extra) != "extra" {
				t.Fatalf("extra is %q, want %q", inflater.Extra, "extra")
			}
			if inflater.Mtime != 1e8 {
				t.Fatalf("mtime is %d, want %d", inflater.Mtime, uint32(1e8))
			}
			if inflater.Name != "name" {
				t.Fatalf("name is %q, want %q", inflater.Name, "name")
			}
		})
}
<|endoftext|>


<commit_before>package main

import ()

var scopToClms = map[string]map[string]bool{
	"profile": {
		"name":               true,
		"family_name":        true,
		"given_name":         true,
		"middle_name":        true,
		"nickname":           true,
		"preferred_username": true,
		"profile":            true,
		"picture":            true,
		"website":            true,
		"gender":             true,
		"birthdate":          true,
		"zoneinfo":           true,
		"locale":             true,
		"updated_at":         true,
	},
	"email": {
		"email":          true,
		"email_verified": true,
	},
	"address": {
		"address": true,
	},
	"phone": {
		"phone_number":          true,
		"phone_number_verified": true,
	},
}

// scope に対応するクレームを返す。
// 返り値は自由に書き換えて良い。
func scopesToClaims(scops map[string]bool) map[string]bool {
	clms := map[string]bool{}
	for scop, ok := range scops {
		if !ok {
			continue
		}
		for clm, ok := range scopToClms[scop] {
			if !ok {
				continue
			}
			clms[clm] = true
		}
	}
	return clms
}
<commit_msg>スコープからクレームへのマップを使用スコープの定義になるように拡張<commit_after>package main

import ()

// サポートするスコープと紐付くクレーム。
var knownScops = map[string]map[string]bool{
	// ID トークンの被発行権。
	"openid": {},
	// リフレッシュトークンの被発行権。
	//"offline_access": {},
	// 以下、クレーム集合の取得権。
	"profile": {
		"name":               true,
		"family_name":        true,
		"given_name":         true,
		"middle_name":        true,
		"nickname":           true,
		"preferred_username": true,
		"profile":            true,
		"picture":            true,
		"website":            true,
		"gender":             true,
		"birthdate":          true,
		"zoneinfo":           true,
		"locale":             true,
		"updated_at":         true,
	},
	"email": {
		"email":          true,
		"email_verified": true,
	},
	"address": {
		"address": true,
	},
	"phone": {
		"phone_number":          true,
		"phone_number_verified": true,
	},
}

// scope に対応するクレームを返す。
// 返り値は自由に書き換えて良い。
func scopesToClaims(scops map[string]bool) map[string]bool {
	clms := map[string]bool{}
	for scop, ok := range scops {
		if !ok {
			continue
		}
		for clm, ok := range knownScops[scop] {
			if !ok {
				continue
			}
			clms[clm] = true
		}
	}
	return clms
}
<|endoftext|>