<commit_before>// Copyright 2019 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 completion

import (
	"context"
	"go/types"
	"strings"
	"time"
)

// MaxDeepCompletions limits deep completion results because in most cases
// there are too many to be useful.
const MaxDeepCompletions = 3

// deepCompletionState stores our state as we search for deep completions.
// "deep completion" refers to searching into objects' fields and methods to
// find more completion candidates.
type deepCompletionState struct {
	// enabled indicates wether deep completion is permitted.
	enabled bool

	// queueClosed is used to disable adding new sub-fields to search queue
	// once we're running out of our time budget.
	queueClosed bool

	// searchQueue holds the current breadth first search queue.
	searchQueue []candidate

	// highScores tracks the highest deep candidate scores we have found
	// so far. This is used to avoid work for low scoring deep candidates.
	highScores [MaxDeepCompletions]float64

	// candidateCount is the count of unique deep candidates encountered
	// so far.
	candidateCount int
}

// enqueue adds a candidate to the search queue.
func (s *deepCompletionState) enqueue(cand candidate) {
	s.searchQueue = append(s.searchQueue, cand)
}

// dequeue removes and returns the leftmost element from the search queue.
func (s *deepCompletionState) dequeue() *candidate {
	var cand *candidate
	cand, s.searchQueue = &s.searchQueue[0], s.searchQueue[1:]
	return cand
}

// scorePenalty computes a deep candidate score penalty. A candidate is
// penalized based on depth to favor shallower candidates. We also give a
// slight bonus to unexported objects and a slight additional penalty to
// function objects.
func (s *deepCompletionState) scorePenalty(cand *candidate) float64 {
	var deepPenalty float64
	for _, dc := range cand.path {
		deepPenalty++

		if !dc.Exported() {
			deepPenalty -= 0.1
		}

		if _, isSig := dc.Type().Underlying().(*types.Signature); isSig {
			deepPenalty += 0.1
		}
	}

	// Normalize penalty to a max depth of 10.
	return deepPenalty / 10
}

// isHighScore returns whether score is among the top MaxDeepCompletions deep
// candidate scores encountered so far. If so, it adds score to highScores,
// possibly displacing an existing high score.
func (s *deepCompletionState) isHighScore(score float64) bool {
	// Invariant: s.highScores is sorted with highest score first. Unclaimed
	// positions are trailing zeros.

	// If we beat an existing score then take its spot.
	for i, deepScore := range s.highScores {
		if score <= deepScore {
			continue
		}

		if deepScore != 0 && i != len(s.highScores)-1 {
			// If this wasn't an empty slot then we need to scooch everyone
			// down one spot.
			copy(s.highScores[i+1:], s.highScores[i:])
		}
		s.highScores[i] = score
		return true
	}

	return false
}

// newPath returns path from search root for an object following a given
// candidate.
func (s *deepCompletionState) newPath(cand *candidate, obj types.Object, invoke bool) ([]types.Object, []string) {
	name := obj.Name()
	if invoke {
		name += "()"
	}

	// copy the slice since we don't want to overwrite the original slice.
	path := append([]types.Object{}, cand.path...)
	names := append([]string{}, cand.names...)

	return append(path, obj), append(names, name)
}

// deepSearch searches a candidate and its subordinate objects for completion
// items if deep completion is enabled and adds the valid candidates to
// completion items.
func (c *completer) deepSearch(ctx context.Context) {
outer:
	for len(c.deepState.searchQueue) > 0 {
		cand := c.deepState.dequeue()
		obj := cand.obj

		// At the top level, dedupe by object.
		if len(cand.path) == 0 {
			if c.seen[cand.obj] {
				continue
			}
			c.seen[cand.obj] = true
		}

		// If obj is not accessible because it lives in another package and is
		// not exported, don't treat it as a completion candidate unless it's
		// a package completion candidate.
		if !c.completionContext.packageCompletion &&
			obj.Pkg() != nil && obj.Pkg() != c.pkg.GetTypes() && !obj.Exported() {
			continue
		}

		// If we want a type name, don't offer non-type name candidates.
		// However, do offer package names since they can contain type names,
		// and do offer any candidate without a type since we aren't sure if it
		// is a type name or not (i.e. unimported candidate).
		if c.wantTypeName() && obj.Type() != nil && !isTypeName(obj) && !isPkgName(obj) {
			continue
		}

		// When searching deep, make sure we don't have a cycle in our chain.
		// We don't dedupe by object because we want to allow both "foo.Baz"
		// and "bar.Baz" even though "Baz" is represented the same types.Object
		// in both.
		for _, seenObj := range cand.path {
			if seenObj == obj {
				continue outer
			}
		}

		c.addCandidate(ctx, cand)

		c.deepState.candidateCount++
		if c.opts.budget > 0 && c.deepState.candidateCount%100 == 0 {
			spent := float64(time.Since(c.startTime)) / float64(c.opts.budget)
			select {
			case <-ctx.Done():
				return
			default:
				// If we are almost out of budgeted time, no further elements
				// should be added to the queue. This ensures remaining time is
				// used for processing current queue.
				if !c.deepState.queueClosed && spent >= 0.85 {
					c.deepState.queueClosed = true
				}
			}
		}

		// if deep search is disabled, don't add any more candidates.
		if !c.deepState.enabled || c.deepState.queueClosed {
			continue
		}

		// Searching members for a type name doesn't make sense.
		if isTypeName(obj) {
			continue
		}
		if obj.Type() == nil {
			continue
		}

		// Don't search embedded fields because they were already included in their
		// parent's fields.
		if v, ok := obj.(*types.Var); ok && v.Embedded() {
			continue
		}

		if sig, ok := obj.Type().Underlying().(*types.Signature); ok {
			// If obj is a function that takes no arguments and returns one
			// value, keep searching across the function call.
			if sig.Params().Len() == 0 && sig.Results().Len() == 1 {
				path, names := c.deepState.newPath(cand, obj, true)
				// The result of a function call is not addressable.
				candidates := c.methodsAndFields(sig.Results().At(0).Type(), false, cand.imp)
				for _, newCand := range candidates {
					newCand.path, newCand.names = path, names
					c.deepState.enqueue(newCand)
				}
			}
		}

		path, names := c.deepState.newPath(cand, obj, false)
		switch obj := obj.(type) {
		case *types.PkgName:
			candidates := c.packageMembers(obj.Imported(), stdScore, cand.imp)
			for _, newCand := range candidates {
				newCand.path, newCand.names = path, names
				c.deepState.enqueue(newCand)
			}
		default:
			candidates := c.methodsAndFields(obj.Type(), cand.addressable, cand.imp)
			for _, newCand := range candidates {
				newCand.path, newCand.names = path, names
				c.deepState.enqueue(newCand)
			}
		}
	}
}

// addCandidate adds a completion candidate to suggestions, without searching
// its members for more candidates.
func (c *completer) addCandidate(ctx context.Context, cand *candidate) {
	obj := cand.obj
	if c.matchingCandidate(cand) {
		cand.score *= highScore

		if p := c.penalty(cand); p > 0 {
			cand.score *= (1 - p)
		}
	} else if isTypeName(obj) {
		// If obj is a *types.TypeName that didn't otherwise match, check
		// if a literal object of this type makes a good candidate.

		// We only care about named types (i.e. don't want builtin types).
		if _, isNamed := obj.Type().(*types.Named); isNamed {
			c.literal(ctx, obj.Type(), cand.imp)
		}
	}

	// Lower score of method calls so we prefer fields and vars over calls.
	if cand.expandFuncCall {
		if sig, ok := obj.Type().Underlying().(*types.Signature); ok && sig.Recv() != nil {
			cand.score *= 0.9
		}
	}

	// Prefer private objects over public ones.
	if !obj.Exported() && obj.Parent() != types.Universe {
		cand.score *= 1.1
	}

	// Favor shallow matches by lowering score according to depth.
	cand.score -= cand.score * c.deepState.scorePenalty(cand)

	if cand.score < 0 {
		cand.score = 0
	}

	cand.name = strings.Join(append(cand.names, cand.obj.Name()), ".")
	if item, err := c.item(ctx, *cand); err == nil {
		c.items = append(c.items, item)
	}
}

// penalty reports a score penalty for cand in the range (0, 1).
// For example, a candidate is penalized if it has already been used
// in another switch case statement.
func (c *completer) penalty(cand *candidate) float64 {
	for _, p := range c.inference.penalized {
		if c.objChainMatches(cand, p.objChain) {
			return p.penalty
		}
	}

	return 0
}

// objChainMatches reports whether cand combined with the surrounding
// object prefix matches chain.
func (c *completer) objChainMatches(cand *candidate, chain []types.Object) bool {
	// For example, when completing:
	//
	//   foo.ba<>
	//
	// If we are considering the deep candidate "bar.baz", cand is baz,
	// objChain is [foo] and deepChain is [bar]. We would match the
	// chain [foo, bar, baz].
	if len(chain) != len(c.inference.objChain)+len(cand.path)+1 {
		return false
	}

	if chain[len(chain)-1] != cand.obj {
		return false
	}

	for i, o := range c.inference.objChain {
		if chain[i] != o {
			return false
		}
	}

	for i, o := range cand.path {
		if chain[i+len(c.inference.objChain)] != o {
			return false
		}
	}

	return true
}
<commit_msg>internal/lsp/source: handle nil pointer exception in completion<commit_after>// Copyright 2019 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 completion

import (
	"context"
	"go/types"
	"strings"
	"time"
)

// MaxDeepCompletions limits deep completion results because in most cases
// there are too many to be useful.
const MaxDeepCompletions = 3

// deepCompletionState stores our state as we search for deep completions.
// "deep completion" refers to searching into objects' fields and methods to
// find more completion candidates.
type deepCompletionState struct {
	// enabled indicates wether deep completion is permitted.
	enabled bool

	// queueClosed is used to disable adding new sub-fields to search queue
	// once we're running out of our time budget.
	queueClosed bool

	// searchQueue holds the current breadth first search queue.
	searchQueue []candidate

	// highScores tracks the highest deep candidate scores we have found
	// so far. This is used to avoid work for low scoring deep candidates.
	highScores [MaxDeepCompletions]float64

	// candidateCount is the count of unique deep candidates encountered
	// so far.
	candidateCount int
}

// enqueue adds a candidate to the search queue.
func (s *deepCompletionState) enqueue(cand candidate) {
	s.searchQueue = append(s.searchQueue, cand)
}

// dequeue removes and returns the leftmost element from the search queue.
func (s *deepCompletionState) dequeue() *candidate {
	var cand *candidate
	cand, s.searchQueue = &s.searchQueue[0], s.searchQueue[1:]
	return cand
}

// scorePenalty computes a deep candidate score penalty. A candidate is
// penalized based on depth to favor shallower candidates. We also give a
// slight bonus to unexported objects and a slight additional penalty to
// function objects.
func (s *deepCompletionState) scorePenalty(cand *candidate) float64 {
	var deepPenalty float64
	for _, dc := range cand.path {
		deepPenalty++

		if !dc.Exported() {
			deepPenalty -= 0.1
		}

		if _, isSig := dc.Type().Underlying().(*types.Signature); isSig {
			deepPenalty += 0.1
		}
	}

	// Normalize penalty to a max depth of 10.
	return deepPenalty / 10
}

// isHighScore returns whether score is among the top MaxDeepCompletions deep
// candidate scores encountered so far. If so, it adds score to highScores,
// possibly displacing an existing high score.
func (s *deepCompletionState) isHighScore(score float64) bool {
	// Invariant: s.highScores is sorted with highest score first. Unclaimed
	// positions are trailing zeros.

	// If we beat an existing score then take its spot.
	for i, deepScore := range s.highScores {
		if score <= deepScore {
			continue
		}

		if deepScore != 0 && i != len(s.highScores)-1 {
			// If this wasn't an empty slot then we need to scooch everyone
			// down one spot.
			copy(s.highScores[i+1:], s.highScores[i:])
		}
		s.highScores[i] = score
		return true
	}

	return false
}

// newPath returns path from search root for an object following a given
// candidate.
func (s *deepCompletionState) newPath(cand *candidate, obj types.Object, invoke bool) ([]types.Object, []string) {
	name := obj.Name()
	if invoke {
		name += "()"
	}

	// copy the slice since we don't want to overwrite the original slice.
	path := append([]types.Object{}, cand.path...)
	names := append([]string{}, cand.names...)

	return append(path, obj), append(names, name)
}

// deepSearch searches a candidate and its subordinate objects for completion
// items if deep completion is enabled and adds the valid candidates to
// completion items.
func (c *completer) deepSearch(ctx context.Context) {
outer:
	for len(c.deepState.searchQueue) > 0 {
		cand := c.deepState.dequeue()
		obj := cand.obj

		if obj == nil {
			continue
		}

		// At the top level, dedupe by object.
		if len(cand.path) == 0 {
			if c.seen[obj] {
				continue
			}
			c.seen[obj] = true
		}

		// If obj is not accessible because it lives in another package and is
		// not exported, don't treat it as a completion candidate unless it's
		// a package completion candidate.
		if !c.completionContext.packageCompletion &&
			obj.Pkg() != nil && obj.Pkg() != c.pkg.GetTypes() && !obj.Exported() {
			continue
		}

		// If we want a type name, don't offer non-type name candidates.
		// However, do offer package names since they can contain type names,
		// and do offer any candidate without a type since we aren't sure if it
		// is a type name or not (i.e. unimported candidate).
		if c.wantTypeName() && obj.Type() != nil && !isTypeName(obj) && !isPkgName(obj) {
			continue
		}

		// When searching deep, make sure we don't have a cycle in our chain.
		// We don't dedupe by object because we want to allow both "foo.Baz"
		// and "bar.Baz" even though "Baz" is represented the same types.Object
		// in both.
		for _, seenObj := range cand.path {
			if seenObj == obj {
				continue outer
			}
		}

		c.addCandidate(ctx, cand)

		c.deepState.candidateCount++
		if c.opts.budget > 0 && c.deepState.candidateCount%100 == 0 {
			spent := float64(time.Since(c.startTime)) / float64(c.opts.budget)
			select {
			case <-ctx.Done():
				return
			default:
				// If we are almost out of budgeted time, no further elements
				// should be added to the queue. This ensures remaining time is
				// used for processing current queue.
				if !c.deepState.queueClosed && spent >= 0.85 {
					c.deepState.queueClosed = true
				}
			}
		}

		// if deep search is disabled, don't add any more candidates.
		if !c.deepState.enabled || c.deepState.queueClosed {
			continue
		}

		// Searching members for a type name doesn't make sense.
		if isTypeName(obj) {
			continue
		}
		if obj.Type() == nil {
			continue
		}

		// Don't search embedded fields because they were already included in their
		// parent's fields.
		if v, ok := obj.(*types.Var); ok && v.Embedded() {
			continue
		}

		if sig, ok := obj.Type().Underlying().(*types.Signature); ok {
			// If obj is a function that takes no arguments and returns one
			// value, keep searching across the function call.
			if sig.Params().Len() == 0 && sig.Results().Len() == 1 {
				path, names := c.deepState.newPath(cand, obj, true)
				// The result of a function call is not addressable.
				candidates := c.methodsAndFields(sig.Results().At(0).Type(), false, cand.imp)
				for _, newCand := range candidates {
					newCand.path, newCand.names = path, names
					c.deepState.enqueue(newCand)
				}
			}
		}

		path, names := c.deepState.newPath(cand, obj, false)
		switch obj := obj.(type) {
		case *types.PkgName:
			candidates := c.packageMembers(obj.Imported(), stdScore, cand.imp)
			for _, newCand := range candidates {
				newCand.path, newCand.names = path, names
				c.deepState.enqueue(newCand)
			}
		default:
			candidates := c.methodsAndFields(obj.Type(), cand.addressable, cand.imp)
			for _, newCand := range candidates {
				newCand.path, newCand.names = path, names
				c.deepState.enqueue(newCand)
			}
		}
	}
}

// addCandidate adds a completion candidate to suggestions, without searching
// its members for more candidates.
func (c *completer) addCandidate(ctx context.Context, cand *candidate) {
	obj := cand.obj
	if c.matchingCandidate(cand) {
		cand.score *= highScore

		if p := c.penalty(cand); p > 0 {
			cand.score *= (1 - p)
		}
	} else if isTypeName(obj) {
		// If obj is a *types.TypeName that didn't otherwise match, check
		// if a literal object of this type makes a good candidate.

		// We only care about named types (i.e. don't want builtin types).
		if _, isNamed := obj.Type().(*types.Named); isNamed {
			c.literal(ctx, obj.Type(), cand.imp)
		}
	}

	// Lower score of method calls so we prefer fields and vars over calls.
	if cand.expandFuncCall {
		if sig, ok := obj.Type().Underlying().(*types.Signature); ok && sig.Recv() != nil {
			cand.score *= 0.9
		}
	}

	// Prefer private objects over public ones.
	if !obj.Exported() && obj.Parent() != types.Universe {
		cand.score *= 1.1
	}

	// Favor shallow matches by lowering score according to depth.
	cand.score -= cand.score * c.deepState.scorePenalty(cand)

	if cand.score < 0 {
		cand.score = 0
	}

	cand.name = strings.Join(append(cand.names, cand.obj.Name()), ".")
	if item, err := c.item(ctx, *cand); err == nil {
		c.items = append(c.items, item)
	}
}

// penalty reports a score penalty for cand in the range (0, 1).
// For example, a candidate is penalized if it has already been used
// in another switch case statement.
func (c *completer) penalty(cand *candidate) float64 {
	for _, p := range c.inference.penalized {
		if c.objChainMatches(cand, p.objChain) {
			return p.penalty
		}
	}

	return 0
}

// objChainMatches reports whether cand combined with the surrounding
// object prefix matches chain.
func (c *completer) objChainMatches(cand *candidate, chain []types.Object) bool {
	// For example, when completing:
	//
	//   foo.ba<>
	//
	// If we are considering the deep candidate "bar.baz", cand is baz,
	// objChain is [foo] and deepChain is [bar]. We would match the
	// chain [foo, bar, baz].
	if len(chain) != len(c.inference.objChain)+len(cand.path)+1 {
		return false
	}

	if chain[len(chain)-1] != cand.obj {
		return false
	}

	for i, o := range c.inference.objChain {
		if chain[i] != o {
			return false
		}
	}

	for i, o := range cand.path {
		if chain[i+len(c.inference.objChain)] != o {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package crlWorker

import (
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "crlWorker"
	workerDesc = "Checks certificate CRL (Certificate Revocation List) and reports on certificate revocation"

	httpClient = &http.Client{
		Timeout: 30 * time.Second,
	}
)

type Result struct {
	RevocationTime time.Time
	Revoked bool `json:"revoked"`
}

func init() {
	runner := new(eval)
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

// Run implements the worker.Worker interface and is called check CRL status
func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	crlRes := Result{}

	// Grab first CRL response we can from our certificate
	crlResponses, err := getCRLResponses(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error getting CRL response, err=%v", err)},
			Result:     nil,
		}
		return
	}
	if len(crlResponses) == 0 {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("no CRL responses received, err=%v", err)},
			Result:     nil,
		}
		return
	}

	// TODO(adam): store all CRL responses
	// TODO(adam): Process all responses? Or just one that's signed?

	certList, err := x509.ParseCRL(crlResponses[0])
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error reading CRL response for %s", in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Verify the CRL
	if err = verifyCRL(certList, in.Certificate, in.CertificateChain); err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error verifying CRL, err=%v", err)},
			Result:     nil,
		}
		return
	}

	x509Cert, err := in.Certificate.ToX509()
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error converting Certificate %s to x509.Certificate, err=%v", in.Certificate.ID, err)},
			Result:     nil,
		}
		return
	}

	// Check if our certificate is in the revoked list
	revoked := certList.TBSCertList.RevokedCertificates
	for i := range revoked {
		if x509Cert.SerialNumber.Cmp(revoked[i].SerialNumber) == 0 {
			// certificate is in revoked list, serials match
			crlRes.RevocationTime = certList.TBSCertList.ThisUpdate
			crlRes.Revoked = true

			if err := in.DBHandle.UpdateCertMarkAsRevoked(in.Certificate.ID); err != nil {
				resChan <- worker.Result{
					Success:    false,
					WorkerName: workerName,
					Errors:     []string{fmt.Sprintf("error update Certificate %s revoked_via_crl, err=%v", in.Certificate.ID, err)},
					Result:     nil,
				}
				return
			}
			break
		}
	}

	// Marshal the response
	bs, err := json.Marshal(&crlRes)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

// Grab the first CRL response and return it in raw bytes
func getCRLResponses(cert certificate.Certificate) ([][]byte, error) {
	var wg sync.WaitGroup
	var out [][]byte

	crlPoints := cert.X509v3Extensions.CRLDistributionPoints
	wg.Add(len(crlPoints))
	for i := range crlPoints {
		go func(point string, wg *sync.WaitGroup) {
			defer wg.Done()

			resp, err := httpClient.Get(point)
			if err != nil {
				return
			}
			defer resp.Body.Close()
			bs, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return
			}
			out = append(out, bs)
		}(crlPoints[i], &wg)
	}
	wg.Wait()

	if len(out) > 0 {
		return out, nil
	}
	return nil, fmt.Errorf("Unable to load CRL data for certificate %s", cert.Subject)
}

func verifyCRL(certList *pkix.CertificateList, cert certificate.Certificate, chain *certificate.Chain) error {
	for i := range chain.Certs {
		// each cert is a base64 DER encoded certificate
		raw, err := base64.StdEncoding.DecodeString(chain.Certs[i])
		if err != nil {
			return fmt.Errorf("error decoding base64 DER of Certificate from %s chain, err=%v", chain.Domain, err)
		}

		block, _ := pem.Decode([]byte(raw))
		if block == nil {
			return fmt.Errorf("error reading DER of Certificate from %s chain, err=%v", chain.Domain, err)
		}
		if block.Type != "CERTIFICATE" {
			return fmt.Errorf("Unknown block %s in %s chain", block.Type, chain.Domain)
		}
		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return fmt.Errorf("error reading certificate, err=%v", err)
		}

		// Check if cert signed the CRL response
		if err := cert.CheckCRLSignature(certList); err == nil {
			return nil // cert signed our CRL
		}
	}
	return fmt.Errorf("Unable to verify CRL against %s chain", chain.Domain)
}

// AnalysisPrinter outputs results of comparing a certificate against the CRL(s) contained within
func (eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("CRL Worker: failed to parse results: err=%v", err)
	}

	if result.Revoked {
		results = append(results, fmt.Sprintf("* Certificate: Revoked (at %s)", result.RevocationTime.String()))
	} else {
		results = append(results, "* Certificate: Not Revoked")
	}
	return results, nil
}
<commit_msg>crlWorker: properly parse Chain certificates<commit_after>package crlWorker

import (
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "crlWorker"
	workerDesc = "Checks certificate CRL (Certificate Revocation List) and reports on certificate revocation"

	httpClient = &http.Client{
		Timeout: 30 * time.Second,
	}
)

type Result struct {
	RevocationTime time.Time
	Revoked bool `json:"revoked"`
}

func init() {
	runner := new(eval)
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

// Run implements the worker.Worker interface and is called check CRL status
func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	crlRes := Result{}

	// Grab first CRL response we can from our certificate
	crlResponses, err := getCRLResponses(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error getting CRL response, err=%v", err)},
			Result:     nil,
		}
		return
	}
	if len(crlResponses) == 0 {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("no CRL responses received, err=%v", err)},
			Result:     nil,
		}
		return
	}

	// TODO(adam): store all CRL responses
	// TODO(adam): Process all responses? Or just one that's signed?

	certList, err := x509.ParseCRL(crlResponses[0])
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error reading CRL response for %s", in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Verify the CRL
	if err = verifyCRL(certList, in.Certificate, in.CertificateChain); err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error verifying CRL, err=%v", err)},
			Result:     nil,
		}
		return
	}

	x509Cert, err := in.Certificate.ToX509()
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("error converting Certificate %s to x509.Certificate, err=%v", in.Certificate.ID, err)},
			Result:     nil,
		}
		return
	}

	// Check if our certificate is in the revoked list
	revoked := certList.TBSCertList.RevokedCertificates
	for i := range revoked {
		if x509Cert.SerialNumber.Cmp(revoked[i].SerialNumber) == 0 {
			// certificate is in revoked list, serials match
			crlRes.RevocationTime = certList.TBSCertList.ThisUpdate
			crlRes.Revoked = true

			if err := in.DBHandle.UpdateCertMarkAsRevoked(in.Certificate.ID); err != nil {
				resChan <- worker.Result{
					Success:    false,
					WorkerName: workerName,
					Errors:     []string{fmt.Sprintf("error update Certificate %s revoked_via_crl, err=%v", in.Certificate.ID, err)},
					Result:     nil,
				}
				return
			}
			break
		}
	}

	// Marshal the response
	bs, err := json.Marshal(&crlRes)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

// Grab the first CRL response and return it in raw bytes
func getCRLResponses(cert certificate.Certificate) ([][]byte, error) {
	var wg sync.WaitGroup
	var out [][]byte

	crlPoints := cert.X509v3Extensions.CRLDistributionPoints
	wg.Add(len(crlPoints))
	for i := range crlPoints {
		go func(point string, wg *sync.WaitGroup) {
			defer wg.Done()

			resp, err := httpClient.Get(point)
			if err != nil {
				return
			}
			defer resp.Body.Close()
			bs, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return
			}
			out = append(out, bs)
		}(crlPoints[i], &wg)
	}
	wg.Wait()

	if len(out) > 0 {
		return out, nil
	}
	return nil, fmt.Errorf("Unable to load CRL data for certificate %s", cert.Subject)
}

func verifyCRL(certList *pkix.CertificateList, cert certificate.Certificate, chain *certificate.Chain) error {
	for i := range chain.Certs {
		// each cert is a base64 DER encoded certificate
		raw, err := base64.StdEncoding.DecodeString(chain.Certs[i])
		if err != nil {
			return fmt.Errorf("error decoding base64 DER of Certificate from %s chain, err=%v", chain.Domain, err)
		}
		cert, err := x509.ParseCertificate(raw)
		if err != nil {
			return fmt.Errorf("error reading certificate, err=%v", err)
		}

		// Check if cert signed the CRL response
		if err := cert.CheckCRLSignature(certList); err == nil {
			return nil // cert signed our CRL
		}
	}
	return fmt.Errorf("Unable to verify CRL against %s chain", chain.Domain)
}

// AnalysisPrinter outputs results of comparing a certificate against the CRL(s) contained within
func (eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("CRL Worker: failed to parse results: err=%v", err)
	}

	if result.Revoked {
		results = append(results, fmt.Sprintf("* Certificate: Revoked (at %s)", result.RevocationTime.String()))
	} else {
		results = append(results, "* Certificate: Not Revoked")
	}
	return results, nil
}
<|endoftext|>


<commit_before>package authentication

import (
	"crypto/tls"
	"fmt"
	"net/url"
	"strings"

	"github.com/clems4ever/authelia/internal/configuration/schema"
	"github.com/clems4ever/authelia/internal/logging"
	"gopkg.in/ldap.v3"
)

// LDAPUserProvider is a provider using a LDAP or AD as a user database.
type LDAPUserProvider struct {
	configuration schema.LDAPAuthenticationBackendConfiguration

	connectionFactory LDAPConnectionFactory
}

// NewLDAPUserProvider creates a new instance of LDAPUserProvider.
func NewLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfiguration) *LDAPUserProvider {
	return &LDAPUserProvider{
		configuration:     configuration,
		connectionFactory: NewLDAPConnectionFactoryImpl(),
	}
}

func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBackendConfiguration,
	connectionFactory LDAPConnectionFactory) *LDAPUserProvider {
	return &LDAPUserProvider{
		configuration:     configuration,
		connectionFactory: connectionFactory,
	}
}

func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) {
	var newConnection LDAPConnection

	url, err := url.Parse(p.configuration.URL)

	if err != nil {
		return nil, fmt.Errorf("Unable to parse URL to LDAP: %s", url)
	}

	if url.Scheme == "ldaps" {
		logging.Logger().Debug("LDAP client starts a TLS session")
		conn, err := p.connectionFactory.DialTLS("tcp", url.Host, &tls.Config{
			InsecureSkipVerify: p.configuration.SkipVerify,
		})
		if err != nil {
			return nil, err
		}
		newConnection = conn
	} else {
		logging.Logger().Debug("LDAP client starts a session over raw TCP")
		conn, err := p.connectionFactory.Dial("tcp", url.Host)
		if err != nil {
			return nil, err
		}
		newConnection = conn
	}

	if err := newConnection.Bind(userDN, password); err != nil {
		return nil, err
	}
	return newConnection, nil
}

// CheckUserPassword checks if provided password matches for the given user.
func (p *LDAPUserProvider) CheckUserPassword(username string, password string) (bool, error) {
	adminClient, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return false, err
	}
	defer adminClient.Close()

	userDN, err := p.getUserDN(adminClient, username)
	if err != nil {
		return false, err
	}

	conn, err := p.connect(userDN, password)
	if err != nil {
		return false, fmt.Errorf("Authentication of user %s failed. Cause: %s", username, err)
	}
	defer conn.Close()

	return true, nil
}

func (p *LDAPUserProvider) getUserAttribute(conn LDAPConnection, username string, attribute string) ([]string, error) {
	client, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	userFilter := strings.Replace(p.configuration.UsersFilter, "{0}", username, -1)
	baseDN := p.configuration.AdditionalUsersDN + "," + p.configuration.BaseDN

	// Search for the given username
	searchRequest := ldap.NewSearchRequest(
		baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
		1, 0, false, userFilter, []string{attribute}, nil,
	)

	sr, err := client.Search(searchRequest)
	if err != nil {
		return nil, fmt.Errorf("Cannot find user DN of user %s. Cause: %s", username, err)
	}

	if len(sr.Entries) != 1 {
		return nil, fmt.Errorf("No %s found for user %s", attribute, username)
	}

	if attribute == "dn" {
		return []string{sr.Entries[0].DN}, nil
	}

	return sr.Entries[0].Attributes[0].Values, nil
}

func (p *LDAPUserProvider) getUserDN(conn LDAPConnection, username string) (string, error) {
	values, err := p.getUserAttribute(conn, username, "dn")

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

	if len(values) != 1 {
		return "", fmt.Errorf("DN attribute of user %s must be set", username)
	}

	return values[0], nil
}

func (p *LDAPUserProvider) getUserUID(conn LDAPConnection, username string) (string, error) {
	values, err := p.getUserAttribute(conn, username, "uid")

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

	if len(values) != 1 {
		return "", fmt.Errorf("UID attribute of user %s must be set", username)
	}

	return values[0], nil
}

func (p *LDAPUserProvider) createGroupsFilter(conn LDAPConnection, username string) (string, error) {
	if strings.Index(p.configuration.GroupsFilter, "{0}") >= 0 {
		return strings.Replace(p.configuration.GroupsFilter, "{0}", username, -1), nil
	} else if strings.Index(p.configuration.GroupsFilter, "{dn}") >= 0 {
		userDN, err := p.getUserDN(conn, username)
		if err != nil {
			return "", err
		}
		return strings.Replace(p.configuration.GroupsFilter, "{dn}", userDN, -1), nil
	} else if strings.Index(p.configuration.GroupsFilter, "{uid}") >= 0 {
		userUID, err := p.getUserUID(conn, username)
		if err != nil {
			return "", err
		}
		return strings.Replace(p.configuration.GroupsFilter, "{uid}", userUID, -1), nil
	}
	return p.configuration.GroupsFilter, nil
}

// GetDetails retrieve the groups a user belongs to.
func (p *LDAPUserProvider) GetDetails(username string) (*UserDetails, error) {
	conn, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	groupsFilter, err := p.createGroupsFilter(conn, username)
	if err != nil {
		return nil, fmt.Errorf("Unable to create group filter for user %s. Cause: %s", username, err)
	}

	groupBaseDN := fmt.Sprintf("%s,%s", p.configuration.AdditionalGroupsDN, p.configuration.BaseDN)

	// Search for the given username
	searchGroupRequest := ldap.NewSearchRequest(
		groupBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
		0, 0, false, groupsFilter, []string{p.configuration.GroupNameAttribute}, nil,
	)

	sr, err := conn.Search(searchGroupRequest)

	if err != nil {
		return nil, fmt.Errorf("Unable to retrieve groups of user %s. Cause: %s", username, err)
	}

	groups := make([]string, 0)

	for _, res := range sr.Entries {
		// append all values of the document. Normally there should be only one per document.
		groups = append(groups, res.Attributes[0].Values...)
	}

	userDN, err := p.getUserDN(conn, username)

	if err != nil {
		return nil, err
	}

	searchEmailRequest := ldap.NewSearchRequest(
		userDN, ldap.ScopeBaseObject, ldap.NeverDerefAliases,
		0, 0, false, "(cn=*)", []string{p.configuration.MailAttribute}, nil,
	)

	sr, err = conn.Search(searchEmailRequest)

	if err != nil {
		return nil, fmt.Errorf("Unable to retrieve email of user %s. Cause: %s", username, err)
	}

	emails := make([]string, 0)

	for _, res := range sr.Entries {
		// append all values of the document. Normally there should be only one per document.
		emails = append(emails, res.Attributes[0].Values...)
	}

	return &UserDetails{
		Emails: emails,
		Groups: groups,
	}, nil
}

// UpdatePassword update the password of the given user.
func (p *LDAPUserProvider) UpdatePassword(username string, newPassword string) error {
	client, err := p.connect(p.configuration.User, p.configuration.Password)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	userDN, err := p.getUserDN(client, username)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	modifyRequest := ldap.NewModifyRequest(userDN, nil)

	modifyRequest.Replace("userPassword", []string{newPassword})

	err = client.Modify(modifyRequest)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	return nil
}
<commit_msg>Allow blank additional_groups_dn and additional_users_dn<commit_after>package authentication

import (
	"crypto/tls"
	"fmt"
	"net/url"
	"strings"

	"github.com/clems4ever/authelia/internal/configuration/schema"
	"github.com/clems4ever/authelia/internal/logging"
	"gopkg.in/ldap.v3"
)

// LDAPUserProvider is a provider using a LDAP or AD as a user database.
type LDAPUserProvider struct {
	configuration schema.LDAPAuthenticationBackendConfiguration

	connectionFactory LDAPConnectionFactory
}

// NewLDAPUserProvider creates a new instance of LDAPUserProvider.
func NewLDAPUserProvider(configuration schema.LDAPAuthenticationBackendConfiguration) *LDAPUserProvider {
	return &LDAPUserProvider{
		configuration:     configuration,
		connectionFactory: NewLDAPConnectionFactoryImpl(),
	}
}

func NewLDAPUserProviderWithFactory(configuration schema.LDAPAuthenticationBackendConfiguration,
	connectionFactory LDAPConnectionFactory) *LDAPUserProvider {
	return &LDAPUserProvider{
		configuration:     configuration,
		connectionFactory: connectionFactory,
	}
}

func (p *LDAPUserProvider) connect(userDN string, password string) (LDAPConnection, error) {
	var newConnection LDAPConnection

	url, err := url.Parse(p.configuration.URL)

	if err != nil {
		return nil, fmt.Errorf("Unable to parse URL to LDAP: %s", url)
	}

	if url.Scheme == "ldaps" {
		logging.Logger().Debug("LDAP client starts a TLS session")
		conn, err := p.connectionFactory.DialTLS("tcp", url.Host, &tls.Config{
			InsecureSkipVerify: p.configuration.SkipVerify,
		})
		if err != nil {
			return nil, err
		}
		newConnection = conn
	} else {
		logging.Logger().Debug("LDAP client starts a session over raw TCP")
		conn, err := p.connectionFactory.Dial("tcp", url.Host)
		if err != nil {
			return nil, err
		}
		newConnection = conn
	}

	if err := newConnection.Bind(userDN, password); err != nil {
		return nil, err
	}
	return newConnection, nil
}

// CheckUserPassword checks if provided password matches for the given user.
func (p *LDAPUserProvider) CheckUserPassword(username string, password string) (bool, error) {
	adminClient, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return false, err
	}
	defer adminClient.Close()

	userDN, err := p.getUserDN(adminClient, username)
	if err != nil {
		return false, err
	}

	conn, err := p.connect(userDN, password)
	if err != nil {
		return false, fmt.Errorf("Authentication of user %s failed. Cause: %s", username, err)
	}
	defer conn.Close()

	return true, nil
}

func (p *LDAPUserProvider) getUserAttribute(conn LDAPConnection, username string, attribute string) ([]string, error) {
	client, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	userFilter := strings.Replace(p.configuration.UsersFilter, "{0}", username, -1)
	var baseDN string = p.configuration.BaseDN
	if p.configuration.AdditionalUsersDN != "" {
		baseDN = p.configuration.AdditionalUsersDN + "," + baseDN
	}

	// Search for the given username
	searchRequest := ldap.NewSearchRequest(
		baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
		1, 0, false, userFilter, []string{attribute}, nil,
	)

	sr, err := client.Search(searchRequest)
	if err != nil {
		return nil, fmt.Errorf("Cannot find user DN of user %s. Cause: %s", username, err)
	}

	if len(sr.Entries) != 1 {
		return nil, fmt.Errorf("No %s found for user %s", attribute, username)
	}

	if attribute == "dn" {
		return []string{sr.Entries[0].DN}, nil
	}

	return sr.Entries[0].Attributes[0].Values, nil
}

func (p *LDAPUserProvider) getUserDN(conn LDAPConnection, username string) (string, error) {
	values, err := p.getUserAttribute(conn, username, "dn")

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

	if len(values) != 1 {
		return "", fmt.Errorf("DN attribute of user %s must be set", username)
	}

	return values[0], nil
}

func (p *LDAPUserProvider) getUserUID(conn LDAPConnection, username string) (string, error) {
	values, err := p.getUserAttribute(conn, username, "uid")

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

	if len(values) != 1 {
		return "", fmt.Errorf("UID attribute of user %s must be set", username)
	}

	return values[0], nil
}

func (p *LDAPUserProvider) createGroupsFilter(conn LDAPConnection, username string) (string, error) {
	if strings.Index(p.configuration.GroupsFilter, "{0}") >= 0 {
		return strings.Replace(p.configuration.GroupsFilter, "{0}", username, -1), nil
	} else if strings.Index(p.configuration.GroupsFilter, "{dn}") >= 0 {
		userDN, err := p.getUserDN(conn, username)
		if err != nil {
			return "", err
		}
		return strings.Replace(p.configuration.GroupsFilter, "{dn}", userDN, -1), nil
	} else if strings.Index(p.configuration.GroupsFilter, "{uid}") >= 0 {
		userUID, err := p.getUserUID(conn, username)
		if err != nil {
			return "", err
		}
		return strings.Replace(p.configuration.GroupsFilter, "{uid}", userUID, -1), nil
	}
	return p.configuration.GroupsFilter, nil
}

// GetDetails retrieve the groups a user belongs to.
func (p *LDAPUserProvider) GetDetails(username string) (*UserDetails, error) {
	conn, err := p.connect(p.configuration.User, p.configuration.Password)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	groupsFilter, err := p.createGroupsFilter(conn, username)
	if err != nil {
		return nil, fmt.Errorf("Unable to create group filter for user %s. Cause: %s", username, err)
	}

	var groupBaseDN string = p.configuration.BaseDN
	if p.configuration.AdditionalGroupsDN != "" {
		groupBaseDN = p.configuration.AdditionalGroupsDN + "," + groupBaseDN
	}

	// Search for the given username
	searchGroupRequest := ldap.NewSearchRequest(
		groupBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,
		0, 0, false, groupsFilter, []string{p.configuration.GroupNameAttribute}, nil,
	)

	sr, err := conn.Search(searchGroupRequest)

	if err != nil {
		return nil, fmt.Errorf("Unable to retrieve groups of user %s. Cause: %s", username, err)
	}

	groups := make([]string, 0)

	for _, res := range sr.Entries {
		// append all values of the document. Normally there should be only one per document.
		groups = append(groups, res.Attributes[0].Values...)
	}

	userDN, err := p.getUserDN(conn, username)

	if err != nil {
		return nil, err
	}

	searchEmailRequest := ldap.NewSearchRequest(
		userDN, ldap.ScopeBaseObject, ldap.NeverDerefAliases,
		0, 0, false, "(cn=*)", []string{p.configuration.MailAttribute}, nil,
	)

	sr, err = conn.Search(searchEmailRequest)

	if err != nil {
		return nil, fmt.Errorf("Unable to retrieve email of user %s. Cause: %s", username, err)
	}

	emails := make([]string, 0)

	for _, res := range sr.Entries {
		// append all values of the document. Normally there should be only one per document.
		emails = append(emails, res.Attributes[0].Values...)
	}

	return &UserDetails{
		Emails: emails,
		Groups: groups,
	}, nil
}

// UpdatePassword update the password of the given user.
func (p *LDAPUserProvider) UpdatePassword(username string, newPassword string) error {
	client, err := p.connect(p.configuration.User, p.configuration.Password)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	userDN, err := p.getUserDN(client, username)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	modifyRequest := ldap.NewModifyRequest(userDN, nil)

	modifyRequest.Replace("userPassword", []string{newPassword})

	err = client.Modify(modifyRequest)

	if err != nil {
		return fmt.Errorf("Unable to update password. Cause: %s", err)
	}

	return nil
}
<|endoftext|>


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

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package opengl

import (
	"reflect"
	"unsafe"

	"github.com/hajimehoshi/ebiten/internal/graphics"
)

const canUsePBO = true

type pboState struct {
	image     *Image
	mappedPBO uintptr
	mapped    []byte
}

var thePBOState pboState

func (s *pboState) mapPBO(img *Image) {
	w, h := graphics.InternalImageSize(img.width), graphics.InternalImageSize(img.height)
	if img.pbo == *new(buffer) {
		img.pbo = img.driver.context.newPixelBufferObject(w, h)
	}
	s.image = img
	s.mappedPBO = img.driver.context.mapPixelBuffer(img.pbo, img.textureNative)

	if s.mappedPBO == 0 {
		panic("opengl: mapPixelBuffer failed")
	}

	var mapped []byte
	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mapped))
	sh.Data = s.mappedPBO
	sh.Len = 4 * w * h
	sh.Cap = 4 * w * h
	s.mapped = mapped
}

func (s *pboState) draw(pix []byte, x, y, width, height int) {
	w := graphics.InternalImageSize(s.image.width)
	stride := 4 * w
	offset := 4 * (y*w + x)
	for j := 0; j < height; j++ {
		copy(s.mapped[offset+stride*j:offset+stride*j+4*width], pix[4*width*j:4*width*(j+1)])
	}
}

func (s *pboState) unmapPBO() {
	i := s.image
	w, h := graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
	i.driver.context.unmapPixelBuffer(i.pbo, w, h)

	s.image = nil
	s.mappedPBO = 0
	s.mapped = nil
}
<commit_msg>graphicsdriver/opengl: Use smaller PBOs<commit_after>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package opengl

import (
	"reflect"
	"unsafe"
)

const canUsePBO = true

type pboState struct {
	image     *Image
	mappedPBO uintptr
	mapped    []byte
}

var thePBOState pboState

func (s *pboState) mapPBO(img *Image) {
	w, h := img.width, img.height
	if img.pbo == *new(buffer) {
		img.pbo = img.driver.context.newPixelBufferObject(w, h)
	}
	s.image = img
	s.mappedPBO = img.driver.context.mapPixelBuffer(img.pbo, img.textureNative)

	if s.mappedPBO == 0 {
		panic("opengl: mapPixelBuffer failed")
	}

	var mapped []byte
	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mapped))
	sh.Data = s.mappedPBO
	sh.Len = 4 * w * h
	sh.Cap = 4 * w * h
	s.mapped = mapped
}

func (s *pboState) draw(pix []byte, x, y, width, height int) {
	w := s.image.width
	stride := 4 * w
	offset := 4 * (y*w + x)
	for j := 0; j < height; j++ {
		copy(s.mapped[offset+stride*j:offset+stride*j+4*width], pix[4*width*j:4*width*(j+1)])
	}
}

func (s *pboState) unmapPBO() {
	i := s.image
	i.driver.context.unmapPixelBuffer(i.pbo, i.width, i.height)

	s.image = nil
	s.mappedPBO = 0
	s.mapped = nil
}
<|endoftext|>


<commit_before>package pluginrepo_test

import (
	"github.com/cloudfoundry/cli/cf/actors/pluginrepo/pluginrepofakes"
	"github.com/cloudfoundry/cli/cf/configuration/coreconfig"
	"github.com/cloudfoundry/cli/cf/models"

	"github.com/cloudfoundry/cli/cf/commandregistry"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"

	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"

	clipr "github.com/cloudfoundry-incubator/cli-plugin-repo/models"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("repo-plugins", func() {
	var (
		ui                  *testterm.FakeUI
		config              coreconfig.Repository
		requirementsFactory *testreq.FakeReqFactory
		fakePluginRepo      *pluginrepofakes.FakePluginRepo
		deps                commandregistry.Dependency
	)

	updateCommandDependency := func(pluginCall bool) {
		deps.Ui = ui
		deps.Config = config
		deps.PluginRepo = fakePluginRepo
		commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("repo-plugins").SetDependency(deps, pluginCall))
	}

	BeforeEach(func() {
		fakePluginRepo = new(pluginrepofakes.FakePluginRepo)
		ui = &testterm.FakeUI{}
		requirementsFactory = &testreq.FakeReqFactory{}
		config = testconfig.NewRepositoryWithDefaults()

		config.SetPluginRepo(models.PluginRepo{
			Name: "repo1",
			Url:  "",
		})

		config.SetPluginRepo(models.PluginRepo{
			Name: "repo2",
			Url:  "",
		})
	})

	var callRepoPlugins = func(args ...string) bool {
		return testcmd.RunCliCommand("repo-plugins", args, requirementsFactory, updateCommandDependency, false)
	}

	Context("If repo name is provided by '-r'", func() {
		It("list plugins from just the named repo", func() {
			callRepoPlugins("-r", "repo2")

			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("repo2"))
			Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(1))

			Expect(ui.Outputs).To(ContainSubstrings([]string{"Getting plugins from repository 'repo2'"}))
		})
	})

	Context("If no repo name is provided", func() {
		It("list plugins from just the named repo", func() {
			callRepoPlugins()

			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("repo1"))
			Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(2))
			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[1].Name).To(Equal("repo2"))

			Expect(ui.Outputs).To(ContainSubstrings([]string{"Getting plugins from all repositories"}))
		})
	})

	Context("when using the default CF-Community repo", func() {

		BeforeEach(func() {
			fakePluginRepo = new(pluginrepofakes.FakePluginRepo)
			ui = &testterm.FakeUI{}
			requirementsFactory = &testreq.FakeReqFactory{}
			config = testconfig.NewRepositoryWithDefaults()

			config.SetPluginRepo(models.PluginRepo{
				Name: "cf",
				Url:  "http://plugins.cloudfoundry.org",
			})
		})

		It("uses https when pointing to plugins.cloudfoundry.org", func() {
			callRepoPlugins()

			Expect(fakePluginRepo.GetPluginsCallCount()).To(Equal(1))
			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("cf"))
			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Url).To(Equal("https://plugins.cloudfoundry.org"))
			Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(1))
		})
	})

	Context("when GetPlugins returns a list of plugin meta data", func() {
		It("lists all plugin data", func() {
			result := make(map[string][]clipr.Plugin)
			result["repo1"] = []clipr.Plugin{
				clipr.Plugin{
					Name:        "plugin1",
					Description: "none1",
				},
			}
			result["repo2"] = []clipr.Plugin{
				clipr.Plugin{
					Name:        "plugin2",
					Description: "none2",
				},
			}
			fakePluginRepo.GetPluginsReturns(result, []string{})

			callRepoPlugins()

			Expect(ui.Outputs).NotTo(ContainSubstrings([]string{"Logged errors:"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"repo1"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"plugin1"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"repo2"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"plugin2"}))
		})
	})

	Context("If errors are reported back from GetPlugins()", func() {
		It("informs user about the errors", func() {
			fakePluginRepo.GetPluginsReturns(nil, []string{
				"error from repo1",
				"error from repo2",
			})

			callRepoPlugins()

			Expect(ui.Outputs).To(ContainSubstrings([]string{"Logged errors:"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"error from repo1"}))
			Expect(ui.Outputs).To(ContainSubstrings([]string{"error from repo2"}))
		})
	})

})
<commit_msg>Remove RunCliCommand<commit_after>package pluginrepo_test

import (
	"github.com/cloudfoundry/cli/cf/actors/pluginrepo/pluginrepofakes"
	"github.com/cloudfoundry/cli/cf/commands/pluginrepo"
	"github.com/cloudfoundry/cli/cf/configuration/coreconfig"
	"github.com/cloudfoundry/cli/cf/models"

	"github.com/cloudfoundry/cli/cf/commandregistry"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"

	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"

	clipr "github.com/cloudfoundry-incubator/cli-plugin-repo/models"

	"github.com/cloudfoundry/cli/flags"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("repo-plugins", func() {
	var (
		ui                  *testterm.FakeUI
		config              coreconfig.Repository
		requirementsFactory *testreq.FakeReqFactory
		fakePluginRepo      *pluginrepofakes.FakePluginRepo
		deps                commandregistry.Dependency
		cmd                 *pluginrepo.RepoPlugins
		flagContext         flags.FlagContext
	)

	BeforeEach(func() {
		fakePluginRepo = new(pluginrepofakes.FakePluginRepo)
		ui = &testterm.FakeUI{}
		requirementsFactory = &testreq.FakeReqFactory{}
		config = testconfig.NewRepositoryWithDefaults()

		deps = commandregistry.Dependency{
			Ui:         ui,
			Config:     config,
			PluginRepo: fakePluginRepo,
		}

		cmd = new(pluginrepo.RepoPlugins)
		cmd = cmd.SetDependency(deps, false).(*pluginrepo.RepoPlugins)
		flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
	})

	var callRepoPlugins = func(args ...string) error {
		err := flagContext.Parse(args...)
		if err != nil {
			return err
		}

		cmd.Execute(flagContext)
		return nil
	}

	Context("when using the default CF-Community repo", func() {
		BeforeEach(func() {
			config.SetPluginRepo(models.PluginRepo{
				Name: "cf",
				Url:  "http://plugins.cloudfoundry.org",
			})
		})

		It("uses https when pointing to plugins.cloudfoundry.org", func() {
			err := callRepoPlugins()
			Expect(err).NotTo(HaveOccurred())

			Expect(fakePluginRepo.GetPluginsCallCount()).To(Equal(1))
			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("cf"))
			Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Url).To(Equal("https://plugins.cloudfoundry.org"))
			Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(1))
		})
	})

	Context("when using other plugin repos", func() {
		BeforeEach(func() {
			config.SetPluginRepo(models.PluginRepo{
				Name: "repo1",
				Url:  "",
			})

			config.SetPluginRepo(models.PluginRepo{
				Name: "repo2",
				Url:  "",
			})
		})

		Context("If repo name is provided by '-r'", func() {
			It("list plugins from just the named repo", func() {
				err := callRepoPlugins("-r", "repo2")
				Expect(err).NotTo(HaveOccurred())

				Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("repo2"))
				Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(1))

				Expect(ui.Outputs).To(ContainSubstrings([]string{"Getting plugins from repository 'repo2'"}))
			})
		})

		Context("If no repo name is provided", func() {
			It("list plugins from just the named repo", func() {
				err := callRepoPlugins()
				Expect(err).NotTo(HaveOccurred())

				Expect(fakePluginRepo.GetPluginsArgsForCall(0)[0].Name).To(Equal("repo1"))
				Expect(len(fakePluginRepo.GetPluginsArgsForCall(0))).To(Equal(2))
				Expect(fakePluginRepo.GetPluginsArgsForCall(0)[1].Name).To(Equal("repo2"))

				Expect(ui.Outputs).To(ContainSubstrings([]string{"Getting plugins from all repositories"}))
			})
		})

		Context("when GetPlugins returns a list of plugin meta data", func() {
			It("lists all plugin data", func() {
				result := make(map[string][]clipr.Plugin)
				result["repo1"] = []clipr.Plugin{
					clipr.Plugin{
						Name:        "plugin1",
						Description: "none1",
					},
				}
				result["repo2"] = []clipr.Plugin{
					clipr.Plugin{
						Name:        "plugin2",
						Description: "none2",
					},
				}
				fakePluginRepo.GetPluginsReturns(result, []string{})

				err := callRepoPlugins()
				Expect(err).NotTo(HaveOccurred())

				Expect(ui.Outputs).NotTo(ContainSubstrings([]string{"Logged errors:"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"repo1"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"plugin1"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"repo2"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"plugin2"}))
			})
		})

		Context("If errors are reported back from GetPlugins()", func() {
			It("informs user about the errors", func() {
				fakePluginRepo.GetPluginsReturns(nil, []string{
					"error from repo1",
					"error from repo2",
				})

				err := callRepoPlugins()
				Expect(err).NotTo(HaveOccurred())

				Expect(ui.Outputs).To(ContainSubstrings([]string{"Logged errors:"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"error from repo1"}))
				Expect(ui.Outputs).To(ContainSubstrings([]string{"error from repo2"}))
			})
		})
	})
})
<|endoftext|>


<commit_before>package gobet

import (
	"fmt"
	"time"
)

const inplayUrl string = "http://www.betfair.com/inplayservice/v1/eventTimeline?alt=json&eventId=%s&locale=en_GB&productType=EXCHANGE&regionCode=UK&ts=%d"

type EventTimeline struct {
	EventId            int64         `json:"eventId"`
	EventTypeId        int           `json:"eventTypeId"`
	Score              EventScores   `json:"score"`
	TimeElapsed        int           `json:"timeElapsed"`
	ElapsedRegularTime int           `json:"elapsedRegularTime"`
	ElapsedAddedTime   int           `json:"elapsedAddedTime"`
	UpdateDetails      []EventUpdate `json:"updateDetails"`
	Status             string        `json:"status"`
	InPlayMatchStatus  string        `json:"inPlayMatchStatus"`
}

type EventScores struct {
	Home EventScore `json:"home"`
	Away EventScore `json:"away"`
}

type EventScore struct {
	Name              string   `json:"name"`
	Score             string   `json:"score"`
	HalfTimeScore     string   `json:"halfTimeScore"`
	FullTimeScore     string   `json:"fullTimeScore"`
	PenaltiesScore    string   `json:"penaltiesScore"`
	PenaltiesSequence []string `json:"penaltiesSequence"`
	Games             string   `json:"games"`
	Sets              string   `json:"sets"`
}

type EventUpdate struct {
	UpdateTime time.Time `json:"updateTime"`
	UpdateId   int       `json:"updateId"`
	Team       string    `json:"team"`
	TeamName   string    `json:"teamName"`
	MatchTime  int       `json:"matchTime"`
	Type       string    `json:"type"`
	UpdateType string    `json:"updateType"`
}

func (s *Session) GetEventTimeline(eventId string) (EventTimeline, error) {
	url := fmt.Sprintf(inplayUrl, eventId, time.Now().Unix())
	headers := map[string]string{"Content-Type": "application/json", "Accept": "application/json"}
	timeline := EventTimeline{}
	err := DoRequest("GET", url, "", headers, &timeline, s.logger)

	return timeline, err
}
<commit_msg>PenaltiesSequence is bool<commit_after>package gobet

import (
	"fmt"
	"time"
)

const inplayUrl string = "http://www.betfair.com/inplayservice/v1/eventTimeline?alt=json&eventId=%s&locale=en_GB&productType=EXCHANGE&regionCode=UK&ts=%d"

type EventTimeline struct {
	EventId            int64         `json:"eventId"`
	EventTypeId        int           `json:"eventTypeId"`
	Score              EventScores   `json:"score"`
	TimeElapsed        int           `json:"timeElapsed"`
	ElapsedRegularTime int           `json:"elapsedRegularTime"`
	ElapsedAddedTime   int           `json:"elapsedAddedTime"`
	UpdateDetails      []EventUpdate `json:"updateDetails"`
	Status             string        `json:"status"`
	InPlayMatchStatus  string        `json:"inPlayMatchStatus"`
}

type EventScores struct {
	Home EventScore `json:"home"`
	Away EventScore `json:"away"`
}

type EventScore struct {
	Name              string `json:"name"`
	Score             string `json:"score"`
	HalfTimeScore     string `json:"halfTimeScore"`
	FullTimeScore     string `json:"fullTimeScore"`
	PenaltiesScore    string `json:"penaltiesScore"`
	PenaltiesSequence []bool `json:"penaltiesSequence"`
	Games             string `json:"games"`
	Sets              string `json:"sets"`
}

type EventUpdate struct {
	UpdateTime time.Time `json:"updateTime"`
	UpdateId   int       `json:"updateId"`
	Team       string    `json:"team"`
	TeamName   string    `json:"teamName"`
	MatchTime  int       `json:"matchTime"`
	Type       string    `json:"type"`
	UpdateType string    `json:"updateType"`
}

func (s *Session) GetEventTimeline(eventId string) (EventTimeline, error) {
	url := fmt.Sprintf(inplayUrl, eventId, time.Now().Unix())
	headers := map[string]string{"Content-Type": "application/json", "Accept": "application/json"}
	timeline := EventTimeline{}
	err := DoRequest("GET", url, "", headers, &timeline, s.logger)

	return timeline, err
}
<|endoftext|>


<commit_before>package yorm

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

//Insert  return lastInsertId and error if has
func (ex *executor) Insert(i interface{}, args ...string) (int64, error) {

	q, err := newTableSetter(reflect.ValueOf(i))
	if q == nil {
		return 0, err
	}

	fmt.Println(q, err)

	clause := &bytes.Buffer{}
	clause.WriteString("INSERT INTO ")
	clause.WriteString(q.table)
	clause.WriteString(" SET ")

	fs := &bytes.Buffer{}
	dests := []interface{}{}

	e := reflect.ValueOf(i).Elem()

	var pk reflect.Value
	var columns []*column

	if len(args) == 0 {
		columns = q.columns

	} else {
		for _, arg := range args {
			arg = strings.ToLower(arg)
			for _, c := range q.columns {
				if strings.ToLower(c.fieldName) == arg || strings.ToLower(c.name) == arg {
					columns = append(columns, c)
				}
			}
		}
	}
	for _, c := range columns {
		v := e.FieldByName(c.fieldName)
		if c.isAuto {
			pk = v
			continue
		}
		fs.WriteString("," + c.name + "=?")
		dests = append(dests, fmt.Sprintf("%v", v.Interface()))
	}
	if fs.Len() == 0 {
		return 0, errors.New("no filed to insert")
	}

	clause.Write(fs.Bytes()[1:])

	r, err := ex.exec(clause.String(), dests...)
	if err != nil {
		return 0, err
	}
	id, err := r.LastInsertId()
	if id > 0 && pk.IsValid() {
		pk.SetInt(id)
	}
	return id, err
}

//Insert insert a record.
func Insert(i interface{}, args ...string) (int64, error) {
	return defaultExecutor.Insert(i, args...)
}
<commit_msg>bug fixed<commit_after>package yorm

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

//Insert  return lastInsertId and error if has
func (ex *executor) Insert(i interface{}, args ...string) (int64, error) {

	q, err := newTableSetter(reflect.ValueOf(i))
	if q == nil {
		return 0, err
	}

	clause := &bytes.Buffer{}
	clause.WriteString("INSERT INTO ")
	clause.WriteString(q.table)
	clause.WriteString(" SET ")

	fs := &bytes.Buffer{}
	dests := []interface{}{}

	e := reflect.ValueOf(i).Elem()

	var pk reflect.Value
	var columns []*column

	if len(args) == 0 {
		columns = q.columns

	} else {
		for _, arg := range args {
			arg = strings.ToLower(arg)
			for _, c := range q.columns {
				if strings.ToLower(c.fieldName) == arg || strings.ToLower(c.name) == arg {
					columns = append(columns, c)
				}
			}
		}
	}
	for _, c := range columns {
		v := e.FieldByName(c.fieldName)
		if c.isAuto {
			pk = v
			continue
		}
		fs.WriteString("," + c.name + "=?")
		dests = append(dests, fmt.Sprintf("%v", v.Interface()))
	}
	if fs.Len() == 0 {
		return 0, errors.New("no filed to insert")
	}

	clause.Write(fs.Bytes()[1:])

	r, err := ex.exec(clause.String(), dests...)
	if err != nil {
		return 0, err
	}
	id, err := r.LastInsertId()
	if id > 0 && pk.IsValid() {
		pk.SetInt(id)
	}
	return id, err
}

//Insert insert a record.
func Insert(i interface{}, args ...string) (int64, error) {
	return defaultExecutor.Insert(i, args...)
}
<|endoftext|>


<commit_before>package ironmq

import (
	"bytes"
	"fmt"
	"http"
	"json"
	"os"
	"path"
)

// A Client contains an Iron.io project ID and a token for authentication.
type Client struct {
	projectId string
	token     string
}

// NewClient returns a new Client using the given project ID and token.
// The network is not used during this call.
func NewClient(projectId, token string) *Client {
	return &Client{projectId, token}
}

type Error struct {
	Status int
	Msg    string
}

func (e *Error) String() string { return fmt.Sprintf("Status %d: %s", e.Status, e.Msg) }

func (c *Client) req(method, endpoint string, body []byte) (map[string]interface{}, os.Error) {
	const host = "mq-aws-us-east-1.iron.io"
	const apiVersion = "1"
	url := path.Join(host, apiVersion, "projects", c.projectId, endpoint)
	url = "http://" + url + "?oauth=" + c.token
	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	//dump, _ := http.DumpResponse(resp, true)
	//fmt.Printf("%s\n", dump)

	jDecoder := json.NewDecoder(resp.Body)
	data := map[string]interface{}{}
	err = jDecoder.Decode(&data)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		msg, _ := data["msg"].(string)
		return nil, &Error{resp.StatusCode, msg}
	}
	return data, nil
}

// Queue represents an IronMQ queue.
type Queue struct {
	name string
	c    *Client
}

// Queue returns a Queue using the given name.
// The network is not used during this call.
func (c *Client) Queue(name string) *Queue {
	return &Queue{name, c}
}

// Get takes one Message off of the queue. The Message will be returned to the queue
// if not deleted before the item's timeout.
func (q *Queue) Get() (*Message, os.Error) {
	resp, err := q.c.req("GET", "queues/"+q.name+"/messages", nil)
	if err != nil {
		return nil, err
	}
	var body string
	body, ok := resp["body"].(string)
	if !ok {
		return nil, os.NewError("Body is not a string")
	}
	id, ok := resp["id"].(string)
	if !ok {
		return nil, os.NewError("ID is not a string")
	}
	return &Message{Id: id, Body: body, q: q}, nil
}

func (q *Queue) push(msg *Message) os.Error {
	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = q.c.req("POST", "queues/"+q.name+"/messages", data)
	if err != nil {
		return err
	}
	return nil
}

// Push adds a message to the queue using IronMQ's default timeout of 10 minutes.
func (q *Queue) Push(msg string) os.Error {
	return q.push(&Message{Body: msg})
}

func (q *Queue) PushTimeout(msg string, timeout int64) os.Error {
	return q.push(&Message{Body: msg, Timeout: timeout})
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time allowed for processing a message.
	Timeout int64 `json:"timeout,omitempty"`
	q       *Queue
}

func (m *Message) Delete() os.Error {
	_, err := m.q.c.req("DELETE", "queues/"+m.q.name+"/messages/"+m.Id, nil)
	return err
}
<commit_msg>Close response body<commit_after>package ironmq

import (
	"bytes"
	"fmt"
	"http"
	"json"
	"os"
	"path"
)

// A Client contains an Iron.io project ID and a token for authentication.
type Client struct {
	projectId string
	token     string
}

// NewClient returns a new Client using the given project ID and token.
// The network is not used during this call.
func NewClient(projectId, token string) *Client {
	return &Client{projectId, token}
}

type Error struct {
	Status int
	Msg    string
}

func (e *Error) String() string { return fmt.Sprintf("Status %d: %s", e.Status, e.Msg) }

func (c *Client) req(method, endpoint string, body []byte) (map[string]interface{}, os.Error) {
	const host = "mq-aws-us-east-1.iron.io"
	const apiVersion = "1"
	url := path.Join(host, apiVersion, "projects", c.projectId, endpoint)
	url = "http://" + url + "?oauth=" + c.token
	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	//dump, _ := http.DumpResponse(resp, true)
	//fmt.Printf("%s\n", dump)

	jDecoder := json.NewDecoder(resp.Body)
	data := map[string]interface{}{}
	err = jDecoder.Decode(&data)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	if resp.StatusCode != 200 {
		msg, _ := data["msg"].(string)
		return nil, &Error{resp.StatusCode, msg}
	}
	return data, nil
}

// Queue represents an IronMQ queue.
type Queue struct {
	name string
	c    *Client
}

// Queue returns a Queue using the given name.
// The network is not used during this call.
func (c *Client) Queue(name string) *Queue {
	return &Queue{name, c}
}

// Get takes one Message off of the queue. The Message will be returned to the queue
// if not deleted before the item's timeout.
func (q *Queue) Get() (*Message, os.Error) {
	resp, err := q.c.req("GET", "queues/"+q.name+"/messages", nil)
	if err != nil {
		return nil, err
	}
	var body string
	body, ok := resp["body"].(string)
	if !ok {
		return nil, os.NewError("Body is not a string")
	}
	id, ok := resp["id"].(string)
	if !ok {
		return nil, os.NewError("ID is not a string")
	}
	return &Message{Id: id, Body: body, q: q}, nil
}

func (q *Queue) push(msg *Message) os.Error {
	data, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = q.c.req("POST", "queues/"+q.name+"/messages", data)
	if err != nil {
		return err
	}
	return nil
}

// Push adds a message to the queue using IronMQ's default timeout of 10 minutes.
func (q *Queue) Push(msg string) os.Error {
	return q.push(&Message{Body: msg})
}

func (q *Queue) PushTimeout(msg string, timeout int64) os.Error {
	return q.push(&Message{Body: msg, Timeout: timeout})
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time allowed for processing a message.
	Timeout int64 `json:"timeout,omitempty"`
	q       *Queue
}

func (m *Message) Delete() os.Error {
	_, err := m.q.c.req("DELETE", "queues/"+m.q.name+"/messages/"+m.Id, nil)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2016 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 acme

import (
	"errors"
	"fmt"
	"net/http"
	"time"
)

// ACME server response statuses used to describe Account, Authorization, and Challenge states.
const (
	StatusUnknown     = "unknown"
	StatusPending     = "pending"
	StatusProcessing  = "processing"
	StatusValid       = "valid"
	StatusInvalid     = "invalid"
	StatusRevoked     = "revoked"
	StatusDeactivated = "deactivated"
)

// CRLReasonCode identifies the reason for a certificate revocation.
type CRLReasonCode int

// CRL reason codes as defined in RFC 5280.
const (
	CRLReasonUnspecified          CRLReasonCode = 0
	CRLReasonKeyCompromise        CRLReasonCode = 1
	CRLReasonCACompromise         CRLReasonCode = 2
	CRLReasonAffiliationChanged   CRLReasonCode = 3
	CRLReasonSuperseded           CRLReasonCode = 4
	CRLReasonCessationOfOperation CRLReasonCode = 5
	CRLReasonCertificateHold      CRLReasonCode = 6
	CRLReasonRemoveFromCRL        CRLReasonCode = 8
	CRLReasonPrivilegeWithdrawn   CRLReasonCode = 9
	CRLReasonAACompromise         CRLReasonCode = 10
)

// ErrUnsupportedKey is returned when an unsupported key type is encountered.
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")

// Error is an ACME error as defined in RFC 7807, Problem Details for HTTP APIs.
type Error struct {
	// StatusCode is The HTTP status code generated by the origin server.
	StatusCode int

	// Type is a URI that identifies the problem type, typically in
	// a "urn:ietf:params:acme:error:xxx" form.
	Type string

	// Detail is a human-readable explanation specific to this occurrence of the problem.
	Detail string

	// Subproblems is an optional list of additional error information, usually
	// indicating problems with specific identifiers during authorization.
	Subproblems []Subproblem

	// Header is the original server error response headers.
	// It may be nil.
	Header http.Header
}

func (e *Error) Error() string {
	return fmt.Sprintf("acme: %s: %s", e.Type, e.Detail)
}

// An Subproblem is additional error detail that is included in an Error,
// usually indicating a problem with a specific identifier during authorization.
type Subproblem struct {
	// Type is a URI that identifies the subproblem type, typically in
	// "urn:ietf:params:acme:error:xxx" form.
	Type string

	// Detail is a human-readable explanation specific to this occurrence of the
	// subproblem.
	Detail string

	// Identifier optionally indicates the identifier that this subproblem is about.
	Identifier *AuthzID
}

// OrderInvalidError is returned when an order is marked as invalid.
type OrderInvalidError struct {
	// Order is the order that is invalid.
	Order *Order
}

func (e OrderInvalidError) Error() string {
	if e.Order == nil || e.Order.Error == nil {
		return "acme: order is invalid"
	}
	return fmt.Sprintf("acme: invalid order (%s): %s", e.Order.Error.Type, e.Order.Error.Detail)
}

// OrderPendingError is returned when an order is still pending after an
// attempted finalization.
type OrderPendingError struct {
	// Order is the order that is pending.
	Order *Order
}

func (e OrderPendingError) Error() string {
	return "acme: order is pending due to incomplete authorization"
}

// AuthorizationError is returned when an authorization is marked as invalid.
type AuthorizationError struct {
	// Authorization is the authorization that is invalid.
	Authorization *Authorization
}

func (e AuthorizationError) Error() string {
	if e.Authorization == nil {
		return "acme: authorization is invalid"
	}
	return fmt.Sprintf("acme: authorization for identifier %s is %s", e.Authorization.Identifier.Value, e.Authorization.Status)
}

// RateLimit reports whether err represents a rate limit error and
// any Retry-After duration returned by the server.
//
// See the following for more details on rate limiting:
// https://tools.ietf.org/html/draft-ietf-acme-acme-09#section-6.5
func RateLimit(err error) (time.Time, bool) {
	e, ok := err.(*Error)
	if !ok || e.Type != "urn:ietf:params:acme:error:rateLimited" {
		return time.Time{}, false
	}
	if e.Header == nil {
		return time.Time{}, true
	}
	return retryAfter(e.Header.Get("Retry-After")), true
}

// Account is a user account. It is associated with a private key.
type Account struct {
	// URL uniquely identifies the account.
	URL string

	// Status is the status of the account. Valid values are StatusValid,
	// StatusDeactivated, and StatusRevoked.
	Status string

	// Contact is a list of URLs that the server can use to contact the client
	// for issues related to this account.
	Contact []string

	// TermsAgreed indicates agreement with the terms of service. It is not
	// modifiable after account creation.
	TermsAgreed bool

	// OrdersURL is the URL used to fetch a list of orders submitted by this
	// account.
	OrdersURL string
}

// Directory is ACME server discovery data.
type Directory struct {
	// NewNonceURL is used to retrieve new nonces.
	NewNonceURL string

	// NewAccountURL is used to create new accounts.
	NewAccountURL string

	// NewOrderURL is used to create new orders.
	NewOrderURL string

	// NewAuthzURL is used to create new authorizations.
	NewAuthzURL string

	// RevokeCertURL is used to revoke a certificate.
	RevokeCertURL string

	// KeyChangeURL is used to change the account key.
	KeyChangeURL string

	// Terms is a URL identifying the current terms of service.
	Terms string

	// Website is an HTTP or HTTPS URL locating a website
	// providing more information about the ACME server.
	Website string

	// CAA consists of lowercase hostname elements, which the ACME server
	// recognises as referring to itself for the purposes of CAA record validation
	// as defined in RFC6844.
	CAA []string

	// ExternalAccountRequired, if true, indicates that the CA requires that all
	// new account requests include an ExternalAccountBinding field associating
	// the new account with an external account.
	ExternalAccountRequired bool
}

// NewOrder creates a new order with the domains provided, suitable for creating
// a TLS certificate order with CreateOrder.
func NewOrder(domains ...string) *Order {
	o := &Order{Identifiers: make([]AuthzID, len(domains))}
	for i, d := range domains {
		o.Identifiers[i] = AuthzID{
			Type:  "dns",
			Value: d,
		}
	}
	return o
}

// An Order represents a request for a certificate and is used to track the
// progress through to issuance.
type Order struct {
	// URL uniquely identifies the order.
	URL string

	// Status is the status of the order. It will be one of StatusPending,
	// StatusProcessing, StatusValid, and StatusInvalid.
	Status string

	// Expires is the teimstamp after which the server will consider the order invalid.
	Expires time.Time

	// Identifiers is a list of identifiers that the order pertains to.
	Identifiers []AuthzID

	// NotBefore is an optional requested value of the notBefore field in the certificate.
	NotBefore time.Time

	// NotAfter is an optional requested value of the notAfter field in the certificate.
	NotAfter time.Time

	// Error is the error that occurred while processing the order, if any.
	Error *Error

	// Authorizations is a list of URLs for authorizations that the client needs
	// to complete before the requested certificate can be issued. For
	// valid/invalid orders, these are the authorizations that were completed.
	Authorizations []string

	// FinalizeURL is the URL that is used to finalize the Order.
	FinalizeURL string

	// CertificateURL is the URL for the certificate that has been issued in
	// response to this order.
	CertificateURL string

	// RetryAfter is the timestamp, if any, to wait for before fetching this
	// order again.
	RetryAfter time.Time
}

// A Challenge is a CA challenge for an identifier.
type Challenge struct {
	// Type is the challenge type, e.g. "http-01" or "dns-01".
	Type string

	// URL is the URL where a challenge response can be posted.
	URL string

	// Token is a random value that uniquely identifies the challenge.
	Token string

	// Validated is the time at which the server validated this challenge.
	Validated time.Time

	// Status identifies the status of this challenge. Valid values are
	// StatusPending, StatusValid, and StatusInvalid.
	Status string

	// Error indicates the errors that occurred while the server was validating
	// this challenge.
	Error *Error
}

// Authorization encodes an authorization response.
type Authorization struct {
	// URL uniquely identifies the authorization.
	URL string

	// Status is the status of the authorization. Valid values are
	// StatusPending, StatusProcessing, StatusValid, StatusInvalid, and
	// StatusRevoked.
	Status string

	// Identifier is the identifier that the account is authorized to represent.
	Identifier AuthzID

	// Expires is the timestamp after which the server will consider this authorization invalid.
	Expires time.Time

	// Challenges is the list of challenges that the client can fulfill in order
	// to prove posession of the identifier. For valid/invalid authorizations,
	// this is the list of challenges that were used.
	Challenges []*Challenge
}

// AuthzID is an identifier that an account is authorized to represent.
type AuthzID struct {
	Type  string // The type of identifier, e.g. "dns".
	Value string // The identifier itself, e.g. "example.org".
}

type wireAuthzID struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

// wireAuthz is ACME JSON representation of Authorization objects.
type wireAuthz struct {
	Status     string
	Challenges []wireChallenge
	Expires    time.Time
	Identifier struct {
		Type  string
		Value string
	}
}

func (z *wireAuthz) authorization(url string) *Authorization {
	a := &Authorization{
		URL:        url,
		Status:     z.Status,
		Expires:    z.Expires,
		Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
		Challenges: make([]*Challenge, len(z.Challenges)),
	}
	for i, v := range z.Challenges {
		a.Challenges[i] = v.challenge()
	}
	return a
}

// wireChallenge is ACME JSON challenge representation.
type wireChallenge struct {
	URL       string
	Type      string
	Token     string
	Status    string
	Validated time.Time
	Error     *Error
}

func (c *wireChallenge) challenge() *Challenge {
	v := &Challenge{
		URL:       c.URL,
		Type:      c.Type,
		Token:     c.Token,
		Status:    c.Status,
		Validated: c.Validated,
		Error:     c.Error,
	}
	if v.Status == "" {
		v.Status = StatusUnknown
	}
	return v
}

// wireError is a subset of fields of the Problem Details object
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
type wireError struct {
	Status      int
	Type        string
	Detail      string
	Subproblems []Subproblem
}

func (e *wireError) error(h http.Header) *Error {
	return &Error{
		StatusCode:  e.Status,
		Type:        e.Type,
		Detail:      e.Detail,
		Subproblems: e.Subproblems,
		Header:      h,
	}
}

type wireOrder struct {
	Status         string
	Expires        time.Time
	Identifiers    []AuthzID
	NotBefore      time.Time
	NotAfter       time.Time
	Error          *Error
	Authorizations []string
	Finalize       string
	Certificate    string
}

func (o *wireOrder) order(url string, retryHeader string) *Order {
	return &Order{
		URL:            url,
		Status:         o.Status,
		Expires:        o.Expires,
		Identifiers:    o.Identifiers,
		NotBefore:      o.NotBefore,
		NotAfter:       o.NotAfter,
		Error:          o.Error,
		Authorizations: o.Authorizations,
		FinalizeURL:    o.Finalize,
		CertificateURL: o.Certificate,
		RetryAfter:     retryAfter(retryHeader),
	}
}
<commit_msg>Add support for the 'Wildcard' field to ACME library<commit_after>// Copyright 2016 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 acme

import (
	"errors"
	"fmt"
	"net/http"
	"time"
)

// ACME server response statuses used to describe Account, Authorization, and Challenge states.
const (
	StatusUnknown     = "unknown"
	StatusPending     = "pending"
	StatusProcessing  = "processing"
	StatusValid       = "valid"
	StatusInvalid     = "invalid"
	StatusRevoked     = "revoked"
	StatusDeactivated = "deactivated"
)

// CRLReasonCode identifies the reason for a certificate revocation.
type CRLReasonCode int

// CRL reason codes as defined in RFC 5280.
const (
	CRLReasonUnspecified          CRLReasonCode = 0
	CRLReasonKeyCompromise        CRLReasonCode = 1
	CRLReasonCACompromise         CRLReasonCode = 2
	CRLReasonAffiliationChanged   CRLReasonCode = 3
	CRLReasonSuperseded           CRLReasonCode = 4
	CRLReasonCessationOfOperation CRLReasonCode = 5
	CRLReasonCertificateHold      CRLReasonCode = 6
	CRLReasonRemoveFromCRL        CRLReasonCode = 8
	CRLReasonPrivilegeWithdrawn   CRLReasonCode = 9
	CRLReasonAACompromise         CRLReasonCode = 10
)

// ErrUnsupportedKey is returned when an unsupported key type is encountered.
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")

// Error is an ACME error as defined in RFC 7807, Problem Details for HTTP APIs.
type Error struct {
	// StatusCode is The HTTP status code generated by the origin server.
	StatusCode int

	// Type is a URI that identifies the problem type, typically in
	// a "urn:ietf:params:acme:error:xxx" form.
	Type string

	// Detail is a human-readable explanation specific to this occurrence of the problem.
	Detail string

	// Subproblems is an optional list of additional error information, usually
	// indicating problems with specific identifiers during authorization.
	Subproblems []Subproblem

	// Header is the original server error response headers.
	// It may be nil.
	Header http.Header
}

func (e *Error) Error() string {
	return fmt.Sprintf("acme: %s: %s", e.Type, e.Detail)
}

// An Subproblem is additional error detail that is included in an Error,
// usually indicating a problem with a specific identifier during authorization.
type Subproblem struct {
	// Type is a URI that identifies the subproblem type, typically in
	// "urn:ietf:params:acme:error:xxx" form.
	Type string

	// Detail is a human-readable explanation specific to this occurrence of the
	// subproblem.
	Detail string

	// Identifier optionally indicates the identifier that this subproblem is about.
	Identifier *AuthzID
}

// OrderInvalidError is returned when an order is marked as invalid.
type OrderInvalidError struct {
	// Order is the order that is invalid.
	Order *Order
}

func (e OrderInvalidError) Error() string {
	if e.Order == nil || e.Order.Error == nil {
		return "acme: order is invalid"
	}
	return fmt.Sprintf("acme: invalid order (%s): %s", e.Order.Error.Type, e.Order.Error.Detail)
}

// OrderPendingError is returned when an order is still pending after an
// attempted finalization.
type OrderPendingError struct {
	// Order is the order that is pending.
	Order *Order
}

func (e OrderPendingError) Error() string {
	return "acme: order is pending due to incomplete authorization"
}

// AuthorizationError is returned when an authorization is marked as invalid.
type AuthorizationError struct {
	// Authorization is the authorization that is invalid.
	Authorization *Authorization
}

func (e AuthorizationError) Error() string {
	if e.Authorization == nil {
		return "acme: authorization is invalid"
	}
	return fmt.Sprintf("acme: authorization for identifier %s is %s", e.Authorization.Identifier.Value, e.Authorization.Status)
}

// RateLimit reports whether err represents a rate limit error and
// any Retry-After duration returned by the server.
//
// See the following for more details on rate limiting:
// https://tools.ietf.org/html/draft-ietf-acme-acme-09#section-6.5
func RateLimit(err error) (time.Time, bool) {
	e, ok := err.(*Error)
	if !ok || e.Type != "urn:ietf:params:acme:error:rateLimited" {
		return time.Time{}, false
	}
	if e.Header == nil {
		return time.Time{}, true
	}
	return retryAfter(e.Header.Get("Retry-After")), true
}

// Account is a user account. It is associated with a private key.
type Account struct {
	// URL uniquely identifies the account.
	URL string

	// Status is the status of the account. Valid values are StatusValid,
	// StatusDeactivated, and StatusRevoked.
	Status string

	// Contact is a list of URLs that the server can use to contact the client
	// for issues related to this account.
	Contact []string

	// TermsAgreed indicates agreement with the terms of service. It is not
	// modifiable after account creation.
	TermsAgreed bool

	// OrdersURL is the URL used to fetch a list of orders submitted by this
	// account.
	OrdersURL string
}

// Directory is ACME server discovery data.
type Directory struct {
	// NewNonceURL is used to retrieve new nonces.
	NewNonceURL string

	// NewAccountURL is used to create new accounts.
	NewAccountURL string

	// NewOrderURL is used to create new orders.
	NewOrderURL string

	// NewAuthzURL is used to create new authorizations.
	NewAuthzURL string

	// RevokeCertURL is used to revoke a certificate.
	RevokeCertURL string

	// KeyChangeURL is used to change the account key.
	KeyChangeURL string

	// Terms is a URL identifying the current terms of service.
	Terms string

	// Website is an HTTP or HTTPS URL locating a website
	// providing more information about the ACME server.
	Website string

	// CAA consists of lowercase hostname elements, which the ACME server
	// recognises as referring to itself for the purposes of CAA record validation
	// as defined in RFC6844.
	CAA []string

	// ExternalAccountRequired, if true, indicates that the CA requires that all
	// new account requests include an ExternalAccountBinding field associating
	// the new account with an external account.
	ExternalAccountRequired bool
}

// NewOrder creates a new order with the domains provided, suitable for creating
// a TLS certificate order with CreateOrder.
func NewOrder(domains ...string) *Order {
	o := &Order{Identifiers: make([]AuthzID, len(domains))}
	for i, d := range domains {
		o.Identifiers[i] = AuthzID{
			Type:  "dns",
			Value: d,
		}
	}
	return o
}

// An Order represents a request for a certificate and is used to track the
// progress through to issuance.
type Order struct {
	// URL uniquely identifies the order.
	URL string

	// Status is the status of the order. It will be one of StatusPending,
	// StatusProcessing, StatusValid, and StatusInvalid.
	Status string

	// Expires is the teimstamp after which the server will consider the order invalid.
	Expires time.Time

	// Identifiers is a list of identifiers that the order pertains to.
	Identifiers []AuthzID

	// NotBefore is an optional requested value of the notBefore field in the certificate.
	NotBefore time.Time

	// NotAfter is an optional requested value of the notAfter field in the certificate.
	NotAfter time.Time

	// Error is the error that occurred while processing the order, if any.
	Error *Error

	// Authorizations is a list of URLs for authorizations that the client needs
	// to complete before the requested certificate can be issued. For
	// valid/invalid orders, these are the authorizations that were completed.
	Authorizations []string

	// FinalizeURL is the URL that is used to finalize the Order.
	FinalizeURL string

	// CertificateURL is the URL for the certificate that has been issued in
	// response to this order.
	CertificateURL string

	// RetryAfter is the timestamp, if any, to wait for before fetching this
	// order again.
	RetryAfter time.Time
}

// A Challenge is a CA challenge for an identifier.
type Challenge struct {
	// Type is the challenge type, e.g. "http-01" or "dns-01".
	Type string

	// URL is the URL where a challenge response can be posted.
	URL string

	// Token is a random value that uniquely identifies the challenge.
	Token string

	// Validated is the time at which the server validated this challenge.
	Validated time.Time

	// Status identifies the status of this challenge. Valid values are
	// StatusPending, StatusValid, and StatusInvalid.
	Status string

	// Error indicates the errors that occurred while the server was validating
	// this challenge.
	Error *Error
}

// Authorization encodes an authorization response.
type Authorization struct {
	// URL uniquely identifies the authorization.
	URL string

	// Status is the status of the authorization. Valid values are
	// StatusPending, StatusProcessing, StatusValid, StatusInvalid, and
	// StatusRevoked.
	Status string

	// Identifier is the identifier that the account is authorized to represent.
	Identifier AuthzID

	// Expires is the timestamp after which the server will consider this authorization invalid.
	Expires time.Time

	// Challenges is the list of challenges that the client can fulfill in order
	// to prove posession of the identifier. For valid/invalid authorizations,
	// this is the list of challenges that were used.
	Challenges []*Challenge

	// Wildcard is set to true if this authorization is for a 'wildcard' dnsName.
	Wildcard bool
}

// AuthzID is an identifier that an account is authorized to represent.
type AuthzID struct {
	Type  string // The type of identifier, e.g. "dns".
	Value string // The identifier itself, e.g. "example.org".
}

type wireAuthzID struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

// wireAuthz is ACME JSON representation of Authorization objects.
type wireAuthz struct {
	Status     string
	Challenges []wireChallenge
	Expires    time.Time
	Identifier struct {
		Type  string
		Value string
	}
	Wildcard bool
}

func (z *wireAuthz) authorization(url string) *Authorization {
	a := &Authorization{
		URL:        url,
		Status:     z.Status,
		Expires:    z.Expires,
		Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
		Challenges: make([]*Challenge, len(z.Challenges)),
		Wildcard:   z.Wildcard,
	}
	for i, v := range z.Challenges {
		a.Challenges[i] = v.challenge()
	}
	return a
}

// wireChallenge is ACME JSON challenge representation.
type wireChallenge struct {
	URL       string
	Type      string
	Token     string
	Status    string
	Validated time.Time
	Error     *Error
}

func (c *wireChallenge) challenge() *Challenge {
	v := &Challenge{
		URL:       c.URL,
		Type:      c.Type,
		Token:     c.Token,
		Status:    c.Status,
		Validated: c.Validated,
		Error:     c.Error,
	}
	if v.Status == "" {
		v.Status = StatusUnknown
	}
	return v
}

// wireError is a subset of fields of the Problem Details object
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
type wireError struct {
	Status      int
	Type        string
	Detail      string
	Subproblems []Subproblem
}

func (e *wireError) error(h http.Header) *Error {
	return &Error{
		StatusCode:  e.Status,
		Type:        e.Type,
		Detail:      e.Detail,
		Subproblems: e.Subproblems,
		Header:      h,
	}
}

type wireOrder struct {
	Status         string
	Expires        time.Time
	Identifiers    []AuthzID
	NotBefore      time.Time
	NotAfter       time.Time
	Error          *Error
	Authorizations []string
	Finalize       string
	Certificate    string
}

func (o *wireOrder) order(url string, retryHeader string) *Order {
	return &Order{
		URL:            url,
		Status:         o.Status,
		Expires:        o.Expires,
		Identifiers:    o.Identifiers,
		NotBefore:      o.NotBefore,
		NotAfter:       o.NotAfter,
		Error:          o.Error,
		Authorizations: o.Authorizations,
		FinalizeURL:    o.Finalize,
		CertificateURL: o.Certificate,
		RetryAfter:     retryAfter(retryHeader),
	}
}
<|endoftext|>