<commit_before>// Copyright 2015 Matthew Holt and The Caddy 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.

package caddytls

import (
	"context"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/url"
	"strconv"
	"time"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
	"github.com/caddyserver/certmagic"
	"github.com/mholt/acmez"
	"github.com/mholt/acmez/acme"
	"go.uber.org/zap"
)

func init() {
	caddy.RegisterModule(ACMEIssuer{})
}

// ACMEIssuer makes an ACME manager
// for managing certificates using ACME.
//
// TODO: support multiple ACME endpoints (probably
// requires an array of these structs) - caddy would
// also have to load certs from the backup CAs if the
// first one is expired...
type ACMEIssuer struct {
	// The URL to the CA's ACME directory endpoint.
	CA string `json:"ca,omitempty"`

	// The URL to the test CA's ACME directory endpoint.
	// This endpoint is only used during retries if there
	// is a failure using the primary CA.
	TestCA string `json:"test_ca,omitempty"`

	// Your email address, so the CA can contact you if necessary.
	// Not required, but strongly recommended to provide one so
	// you can be reached if there is a problem. Your email is
	// not sent to any Caddy mothership or used for any purpose
	// other than ACME transactions.
	Email string `json:"email,omitempty"`

	// If using an ACME CA that requires an external account
	// binding, specify the CA-provided credentials here.
	ExternalAccount *acme.EAB `json:"external_account,omitempty"`

	// Time to wait before timing out an ACME operation.
	ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`

	// Configures the various ACME challenge types.
	Challenges *ChallengesConfig `json:"challenges,omitempty"`

	// An array of files of CA certificates to accept when connecting to the
	// ACME CA. Generally, you should only use this if the ACME CA endpoint
	// is internal or for development/testing purposes.
	TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`

	rootPool *x509.CertPool
	template certmagic.ACMEManager
	magic    *certmagic.Config
	logger   *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (ACMEIssuer) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "tls.issuance.acme",
		New: func() caddy.Module { return new(ACMEIssuer) },
	}
}

// Provision sets up iss.
func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
	iss.logger = ctx.Logger(iss)

	// DNS providers
	if iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.ProviderRaw != nil {
		val, err := ctx.LoadModule(iss.Challenges.DNS, "ProviderRaw")
		if err != nil {
			return fmt.Errorf("loading DNS provider module: %v", err)
		}

		if deprecatedProvider, ok := val.(acmez.Solver); ok {
			// TODO: For a temporary amount of time, we are allowing the use of DNS
			// providers from go-acme/lego since there are so many providers implemented
			// using that API -- they are adapted as an all-in-one Caddy module in this
			// repository: https://github.com/caddy-dns/lego-deprecated - the module is a
			// acmez.Solver type, so we use it directly. The user must set environment
			// variables to configure it. Remove this shim once a sufficient number of
			// DNS providers are implemented for the libdns APIs instead.
			iss.Challenges.DNS.solver = deprecatedProvider
		} else {
			iss.Challenges.DNS.solver = &certmagic.DNS01Solver{
				DNSProvider:        val.(certmagic.ACMEDNSProvider),
				TTL:                time.Duration(iss.Challenges.DNS.TTL),
				PropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout),
				Resolvers:          iss.Challenges.DNS.Resolvers,
			}
		}
	}

	// add any custom CAs to trust store
	if len(iss.TrustedRootsPEMFiles) > 0 {
		iss.rootPool = x509.NewCertPool()
		for _, pemFile := range iss.TrustedRootsPEMFiles {
			pemData, err := ioutil.ReadFile(pemFile)
			if err != nil {
				return fmt.Errorf("loading trusted root CA's PEM file: %s: %v", pemFile, err)
			}
			if !iss.rootPool.AppendCertsFromPEM(pemData) {
				return fmt.Errorf("unable to add %s to trust pool: %v", pemFile, err)
			}
		}
	}

	var err error
	iss.template, err = iss.makeIssuerTemplate()
	if err != nil {
		return err
	}

	return nil
}

func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
	template := certmagic.ACMEManager{
		CA:                iss.CA,
		TestCA:            iss.TestCA,
		Email:             iss.Email,
		CertObtainTimeout: time.Duration(iss.ACMETimeout),
		TrustedRoots:      iss.rootPool,
		ExternalAccount:   iss.ExternalAccount,
		Logger:            iss.logger,
	}

	if iss.Challenges != nil {
		if iss.Challenges.HTTP != nil {
			template.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled
			template.AltHTTPPort = iss.Challenges.HTTP.AlternatePort
		}
		if iss.Challenges.TLSALPN != nil {
			template.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled
			template.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort
		}
		if iss.Challenges.DNS != nil {
			template.DNS01Solver = iss.Challenges.DNS.solver
		}
		template.ListenHost = iss.Challenges.BindHost
	}

	return template, nil
}

// SetConfig sets the associated certmagic config for this issuer.
// This is required because ACME needs values from the config in
// order to solve the challenges during issuance. This implements
// the ConfigSetter interface.
func (iss *ACMEIssuer) SetConfig(cfg *certmagic.Config) {
	iss.magic = cfg
}

// TODO: I kind of hate how each call to these methods needs to
// make a new ACME manager to fill in defaults before using; can
// we find the right place to do that just once and then re-use?

// PreCheck implements the certmagic.PreChecker interface.
func (iss *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
	return certmagic.NewACMEManager(iss.magic, iss.template).PreCheck(ctx, names, interactive)
}

// Issue obtains a certificate for the given csr.
func (iss *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) {
	return certmagic.NewACMEManager(iss.magic, iss.template).Issue(ctx, csr)
}

// IssuerKey returns the unique issuer key for the configured CA endpoint.
func (iss *ACMEIssuer) IssuerKey() string {
	return certmagic.NewACMEManager(iss.magic, iss.template).IssuerKey()
}

// Revoke revokes the given certificate.
func (iss *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error {
	return certmagic.NewACMEManager(iss.magic, iss.template).Revoke(ctx, cert, reason)
}

// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because
// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface
// with only this method will succeed, and will still allow the embedded ACMEIssuer
// to be accessed and manipulated.
func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }

// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
//
//     ... acme {
//         dir <directory_url>
//         test_dir <test_directory_url>
//         email <email>
//         timeout <duration>
//         disable_http_challenge
//         disable_tlsalpn_challenge
//         alt_http_port    <port>
//         alt_tlsalpn_port <port>
//         eab <key_id> <mac_key>
//         trusted_roots <pem_files...>
//         resolvers <dns_servers...>
//     }
//
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	for d.Next() {
		for nesting := d.Nesting(); d.NextBlock(nesting); {
			switch d.Val() {
			case "dir":
				if !d.AllArgs(&iss.CA) {
					return d.ArgErr()
				}

			case "test_dir":
				if !d.AllArgs(&iss.TestCA) {
					return d.ArgErr()
				}

			case "email":
				if !d.AllArgs(&iss.Email) {
					return d.ArgErr()
				}

			case "timeout":
				var timeoutStr string
				if !d.AllArgs(&timeoutStr) {
					return d.ArgErr()
				}
				timeout, err := caddy.ParseDuration(timeoutStr)
				if err != nil {
					return d.Errf("invalid timeout duration %s: %v", timeoutStr, err)
				}
				iss.ACMETimeout = caddy.Duration(timeout)

			case "disable_http_challenge":
				if d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.HTTP == nil {
					iss.Challenges.HTTP = new(HTTPChallengeConfig)
				}
				iss.Challenges.HTTP.Disabled = true

			case "disable_tlsalpn_challenge":
				if d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.TLSALPN == nil {
					iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
				}
				iss.Challenges.TLSALPN.Disabled = true

			case "alt_http_port":
				if !d.NextArg() {
					return d.ArgErr()
				}
				port, err := strconv.Atoi(d.Val())
				if err != nil {
					return d.Errf("invalid port %s: %v", d.Val(), err)
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.HTTP == nil {
					iss.Challenges.HTTP = new(HTTPChallengeConfig)
				}
				iss.Challenges.HTTP.AlternatePort = port

			case "alt_tlsalpn_port":
				if !d.NextArg() {
					return d.ArgErr()
				}
				port, err := strconv.Atoi(d.Val())
				if err != nil {
					return d.Errf("invalid port %s: %v", d.Val(), err)
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.TLSALPN == nil {
					iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
				}
				iss.Challenges.TLSALPN.AlternatePort = port

			case "eab":
				iss.ExternalAccount = new(acme.EAB)
				if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) {
					return d.ArgErr()
				}

			case "trusted_roots":
				iss.TrustedRootsPEMFiles = d.RemainingArgs()

			case "resolvers":
				if !d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.DNS == nil {
					iss.Challenges.DNS = new(DNSChallengeConfig)
				}
				iss.Challenges.DNS.Resolvers = d.RemainingArgs()

			default:
				return d.Errf("unrecognized ACME issuer property: %s", d.Val())
			}
		}
	}
	return nil
}

// onDemandAskRequest makes a request to the ask URL
// to see if a certificate can be obtained for name.
// The certificate request should be denied if this
// returns an error.
func onDemandAskRequest(ask string, name string) error {
	askURL, err := url.Parse(ask)
	if err != nil {
		return fmt.Errorf("parsing ask URL: %v", err)
	}
	qs := askURL.Query()
	qs.Set("domain", name)
	askURL.RawQuery = qs.Encode()

	resp, err := onDemandAskClient.Get(askURL.String())
	if err != nil {
		return fmt.Errorf("error checking %v to determine if certificate for hostname '%s' should be allowed: %v",
			ask, name, err)
	}
	resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return fmt.Errorf("certificate for hostname '%s' not allowed; non-2xx status code %d returned from %v",
			name, resp.StatusCode, ask)
	}

	return nil
}

// Interface guards
var (
	_ certmagic.PreChecker  = (*ACMEIssuer)(nil)
	_ certmagic.Issuer      = (*ACMEIssuer)(nil)
	_ certmagic.Revoker     = (*ACMEIssuer)(nil)
	_ caddy.Provisioner     = (*ACMEIssuer)(nil)
	_ ConfigSetter          = (*ACMEIssuer)(nil)
	_ caddyfile.Unmarshaler = (*ACMEIssuer)(nil)
)
<commit_msg>caddytls: Add `dns` config to acmeissuer (#3701)<commit_after>// Copyright 2015 Matthew Holt and The Caddy 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.

package caddytls

import (
	"context"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/url"
	"strconv"
	"time"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig"
	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
	"github.com/caddyserver/certmagic"
	"github.com/mholt/acmez"
	"github.com/mholt/acmez/acme"
	"go.uber.org/zap"
)

func init() {
	caddy.RegisterModule(ACMEIssuer{})
}

// ACMEIssuer makes an ACME manager
// for managing certificates using ACME.
//
// TODO: support multiple ACME endpoints (probably
// requires an array of these structs) - caddy would
// also have to load certs from the backup CAs if the
// first one is expired...
type ACMEIssuer struct {
	// The URL to the CA's ACME directory endpoint.
	CA string `json:"ca,omitempty"`

	// The URL to the test CA's ACME directory endpoint.
	// This endpoint is only used during retries if there
	// is a failure using the primary CA.
	TestCA string `json:"test_ca,omitempty"`

	// Your email address, so the CA can contact you if necessary.
	// Not required, but strongly recommended to provide one so
	// you can be reached if there is a problem. Your email is
	// not sent to any Caddy mothership or used for any purpose
	// other than ACME transactions.
	Email string `json:"email,omitempty"`

	// If using an ACME CA that requires an external account
	// binding, specify the CA-provided credentials here.
	ExternalAccount *acme.EAB `json:"external_account,omitempty"`

	// Time to wait before timing out an ACME operation.
	ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`

	// Configures the various ACME challenge types.
	Challenges *ChallengesConfig `json:"challenges,omitempty"`

	// An array of files of CA certificates to accept when connecting to the
	// ACME CA. Generally, you should only use this if the ACME CA endpoint
	// is internal or for development/testing purposes.
	TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`

	rootPool *x509.CertPool
	template certmagic.ACMEManager
	magic    *certmagic.Config
	logger   *zap.Logger
}

// CaddyModule returns the Caddy module information.
func (ACMEIssuer) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "tls.issuance.acme",
		New: func() caddy.Module { return new(ACMEIssuer) },
	}
}

// Provision sets up iss.
func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
	iss.logger = ctx.Logger(iss)

	// DNS providers
	if iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.ProviderRaw != nil {
		val, err := ctx.LoadModule(iss.Challenges.DNS, "ProviderRaw")
		if err != nil {
			return fmt.Errorf("loading DNS provider module: %v", err)
		}

		if deprecatedProvider, ok := val.(acmez.Solver); ok {
			// TODO: For a temporary amount of time, we are allowing the use of DNS
			// providers from go-acme/lego since there are so many providers implemented
			// using that API -- they are adapted as an all-in-one Caddy module in this
			// repository: https://github.com/caddy-dns/lego-deprecated - the module is a
			// acmez.Solver type, so we use it directly. The user must set environment
			// variables to configure it. Remove this shim once a sufficient number of
			// DNS providers are implemented for the libdns APIs instead.
			iss.Challenges.DNS.solver = deprecatedProvider
		} else {
			iss.Challenges.DNS.solver = &certmagic.DNS01Solver{
				DNSProvider:        val.(certmagic.ACMEDNSProvider),
				TTL:                time.Duration(iss.Challenges.DNS.TTL),
				PropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout),
				Resolvers:          iss.Challenges.DNS.Resolvers,
			}
		}
	}

	// add any custom CAs to trust store
	if len(iss.TrustedRootsPEMFiles) > 0 {
		iss.rootPool = x509.NewCertPool()
		for _, pemFile := range iss.TrustedRootsPEMFiles {
			pemData, err := ioutil.ReadFile(pemFile)
			if err != nil {
				return fmt.Errorf("loading trusted root CA's PEM file: %s: %v", pemFile, err)
			}
			if !iss.rootPool.AppendCertsFromPEM(pemData) {
				return fmt.Errorf("unable to add %s to trust pool: %v", pemFile, err)
			}
		}
	}

	var err error
	iss.template, err = iss.makeIssuerTemplate()
	if err != nil {
		return err
	}

	return nil
}

func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
	template := certmagic.ACMEManager{
		CA:                iss.CA,
		TestCA:            iss.TestCA,
		Email:             iss.Email,
		CertObtainTimeout: time.Duration(iss.ACMETimeout),
		TrustedRoots:      iss.rootPool,
		ExternalAccount:   iss.ExternalAccount,
		Logger:            iss.logger,
	}

	if iss.Challenges != nil {
		if iss.Challenges.HTTP != nil {
			template.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled
			template.AltHTTPPort = iss.Challenges.HTTP.AlternatePort
		}
		if iss.Challenges.TLSALPN != nil {
			template.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled
			template.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort
		}
		if iss.Challenges.DNS != nil {
			template.DNS01Solver = iss.Challenges.DNS.solver
		}
		template.ListenHost = iss.Challenges.BindHost
	}

	return template, nil
}

// SetConfig sets the associated certmagic config for this issuer.
// This is required because ACME needs values from the config in
// order to solve the challenges during issuance. This implements
// the ConfigSetter interface.
func (iss *ACMEIssuer) SetConfig(cfg *certmagic.Config) {
	iss.magic = cfg
}

// TODO: I kind of hate how each call to these methods needs to
// make a new ACME manager to fill in defaults before using; can
// we find the right place to do that just once and then re-use?

// PreCheck implements the certmagic.PreChecker interface.
func (iss *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
	return certmagic.NewACMEManager(iss.magic, iss.template).PreCheck(ctx, names, interactive)
}

// Issue obtains a certificate for the given csr.
func (iss *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) {
	return certmagic.NewACMEManager(iss.magic, iss.template).Issue(ctx, csr)
}

// IssuerKey returns the unique issuer key for the configured CA endpoint.
func (iss *ACMEIssuer) IssuerKey() string {
	return certmagic.NewACMEManager(iss.magic, iss.template).IssuerKey()
}

// Revoke revokes the given certificate.
func (iss *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error {
	return certmagic.NewACMEManager(iss.magic, iss.template).Revoke(ctx, cert, reason)
}

// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because
// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface
// with only this method will succeed, and will still allow the embedded ACMEIssuer
// to be accessed and manipulated.
func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }

// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
//
//     ... acme {
//         dir <directory_url>
//         test_dir <test_directory_url>
//         email <email>
//         timeout <duration>
//         disable_http_challenge
//         disable_tlsalpn_challenge
//         alt_http_port    <port>
//         alt_tlsalpn_port <port>
//         eab <key_id> <mac_key>
//         trusted_roots <pem_files...>
//         dns <provider_name> [<options>]
//         resolvers <dns_servers...>
//     }
//
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	for d.Next() {
		for nesting := d.Nesting(); d.NextBlock(nesting); {
			switch d.Val() {
			case "dir":
				if !d.AllArgs(&iss.CA) {
					return d.ArgErr()
				}

			case "test_dir":
				if !d.AllArgs(&iss.TestCA) {
					return d.ArgErr()
				}

			case "email":
				if !d.AllArgs(&iss.Email) {
					return d.ArgErr()
				}

			case "timeout":
				var timeoutStr string
				if !d.AllArgs(&timeoutStr) {
					return d.ArgErr()
				}
				timeout, err := caddy.ParseDuration(timeoutStr)
				if err != nil {
					return d.Errf("invalid timeout duration %s: %v", timeoutStr, err)
				}
				iss.ACMETimeout = caddy.Duration(timeout)

			case "disable_http_challenge":
				if d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.HTTP == nil {
					iss.Challenges.HTTP = new(HTTPChallengeConfig)
				}
				iss.Challenges.HTTP.Disabled = true

			case "disable_tlsalpn_challenge":
				if d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.TLSALPN == nil {
					iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
				}
				iss.Challenges.TLSALPN.Disabled = true

			case "alt_http_port":
				if !d.NextArg() {
					return d.ArgErr()
				}
				port, err := strconv.Atoi(d.Val())
				if err != nil {
					return d.Errf("invalid port %s: %v", d.Val(), err)
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.HTTP == nil {
					iss.Challenges.HTTP = new(HTTPChallengeConfig)
				}
				iss.Challenges.HTTP.AlternatePort = port

			case "alt_tlsalpn_port":
				if !d.NextArg() {
					return d.ArgErr()
				}
				port, err := strconv.Atoi(d.Val())
				if err != nil {
					return d.Errf("invalid port %s: %v", d.Val(), err)
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.TLSALPN == nil {
					iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig)
				}
				iss.Challenges.TLSALPN.AlternatePort = port

			case "eab":
				iss.ExternalAccount = new(acme.EAB)
				if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) {
					return d.ArgErr()
				}

			case "trusted_roots":
				iss.TrustedRootsPEMFiles = d.RemainingArgs()

			case "dns":
				if !d.NextArg() {
					return d.ArgErr()
				}
				provName := d.Val()
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.DNS == nil {
					iss.Challenges.DNS = new(DNSChallengeConfig)
				}
				dnsProvModule, err := caddy.GetModule("dns.providers." + provName)
				if err != nil {
					return d.Errf("getting DNS provider module named '%s': %v", provName, err)
				}
				dnsProvModuleInstance := dnsProvModule.New()
				if unm, ok := dnsProvModuleInstance.(caddyfile.Unmarshaler); ok {
					err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
					if err != nil {
						return err
					}
				}
				iss.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(dnsProvModuleInstance, "name", provName, nil)

			case "resolvers":
				if !d.NextArg() {
					return d.ArgErr()
				}
				if iss.Challenges == nil {
					iss.Challenges = new(ChallengesConfig)
				}
				if iss.Challenges.DNS == nil {
					iss.Challenges.DNS = new(DNSChallengeConfig)
				}
				iss.Challenges.DNS.Resolvers = d.RemainingArgs()

			default:
				return d.Errf("unrecognized ACME issuer property: %s", d.Val())
			}
		}
	}
	return nil
}

// onDemandAskRequest makes a request to the ask URL
// to see if a certificate can be obtained for name.
// The certificate request should be denied if this
// returns an error.
func onDemandAskRequest(ask string, name string) error {
	askURL, err := url.Parse(ask)
	if err != nil {
		return fmt.Errorf("parsing ask URL: %v", err)
	}
	qs := askURL.Query()
	qs.Set("domain", name)
	askURL.RawQuery = qs.Encode()

	resp, err := onDemandAskClient.Get(askURL.String())
	if err != nil {
		return fmt.Errorf("error checking %v to determine if certificate for hostname '%s' should be allowed: %v",
			ask, name, err)
	}
	resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return fmt.Errorf("certificate for hostname '%s' not allowed; non-2xx status code %d returned from %v",
			name, resp.StatusCode, ask)
	}

	return nil
}

// Interface guards
var (
	_ certmagic.PreChecker  = (*ACMEIssuer)(nil)
	_ certmagic.Issuer      = (*ACMEIssuer)(nil)
	_ certmagic.Revoker     = (*ACMEIssuer)(nil)
	_ caddy.Provisioner     = (*ACMEIssuer)(nil)
	_ ConfigSetter          = (*ACMEIssuer)(nil)
	_ caddyfile.Unmarshaler = (*ACMEIssuer)(nil)
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type copyCmd struct {
	profArgs profileList
	confArgs configList
	ephem    bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Copy containers within or in between lxd instances.

lxc copy [remote:]<source container> [[remote:]<destination container>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...]`)
}

func (c *copyCmd) flags() {
	gnuflag.Var(&c.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.profArgs, "profile", i18n.G("Profile to apply to the new container"))
	gnuflag.Var(&c.profArgs, "p", i18n.G("Profile to apply to the new container"))
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
}

func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" && destResource != "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	var status struct {
		Architecture string
		Devices      shared.Devices
		Config       map[string]string
		Profiles     []string
	}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		result, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles

	} else {
		result, err := source.SnapshotInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles
	}

	if c.profArgs != nil {
		status.Profiles = append(status.Profiles, c.profArgs...)
	}

	if configMap != nil {
		for key, value := range configMap {
			status.Config[key] = value
		}
	}

	baseImage = status.Config["volatile.base_image"]

	if !keepVolatile {
		for k := range status.Config {
			if strings.HasPrefix(k, "volatile") {
				delete(status.Config, k)
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1)
		if err != nil {
			return err
		}

		err = source.WaitForSuccess(cp.Operation)
		if err != nil {
			return err
		}

		if destResource == "" {
			op, err := cp.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	dest, err := lxd.NewClient(config, destRemote)
	if err != nil {
		return err
	}

	sourceProfs := shared.NewStringSet(status.Profiles)
	destProfs := []string{}

	profiles, err := dest.ListProfiles()
	if err != nil {
		return err
	}

	for _, profile := range profiles {
		destProfs = append(destProfs, profile.Name)
	}

	if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
		return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
	}

	if ephemeral == -1 {
		ct, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		if ct.Ephemeral {
			ephemeral = 1
		} else {
			ephemeral = 0
		}
	}

	sourceWSResponse, err := source.GetMigrationSourceWS(sourceName)
	if err != nil {
		return err
	}

	secrets := map[string]string{}

	op, err := sourceWSResponse.MetadataAsOperation()
	if err != nil {
		return err
	}

	for k, v := range *op.Metadata {
		secrets[k] = v.(string)
	}

	addresses, err := source.Addresses()
	if err != nil {
		return err
	}

	/* Since we're trying a bunch of different network ports that
	 * may be invalid, we can get "bad handshake" errors when the
	 * websocket code tries to connect. If the first error is a
	 * real error, but the subsequent errors are only network
	 * errors, we should try to report the first real error. Of
	 * course, if all the errors are websocket errors, let's just
	 * report that.
	 */
	for _, addr := range addresses {
		var migration *lxd.Response

		sourceWSUrl := "https://" + addr + sourceWSResponse.Operation
		migration, err = dest.MigrateFrom(destName, sourceWSUrl, source.Certificate, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1)
		if err != nil {
			continue
		}

		if err = dest.WaitForSuccess(migration.Operation); err != nil {
			return err
		}

		if destResource == "" {
			op, err := migration.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	return err
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	if len(args) < 2 {
		return c.copyContainer(config, args[0], "", false, ephem)
	}

	return c.copyContainer(config, args[0], args[1], false, ephem)
}
<commit_msg>lxc/copy: adapt to changes in MigrateFrom()<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type copyCmd struct {
	profArgs profileList
	confArgs configList
	ephem    bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Copy containers within or in between lxd instances.

lxc copy [remote:]<source container> [[remote:]<destination container>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...]`)
}

func (c *copyCmd) flags() {
	gnuflag.Var(&c.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.profArgs, "profile", i18n.G("Profile to apply to the new container"))
	gnuflag.Var(&c.profArgs, "p", i18n.G("Profile to apply to the new container"))
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
}

func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" && destResource != "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	var status struct {
		Architecture string
		Devices      shared.Devices
		Config       map[string]string
		Profiles     []string
	}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		result, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles

	} else {
		result, err := source.SnapshotInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles
	}

	if c.profArgs != nil {
		status.Profiles = append(status.Profiles, c.profArgs...)
	}

	if configMap != nil {
		for key, value := range configMap {
			status.Config[key] = value
		}
	}

	baseImage = status.Config["volatile.base_image"]

	if !keepVolatile {
		for k := range status.Config {
			if strings.HasPrefix(k, "volatile") {
				delete(status.Config, k)
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1)
		if err != nil {
			return err
		}

		err = source.WaitForSuccess(cp.Operation)
		if err != nil {
			return err
		}

		if destResource == "" {
			op, err := cp.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	dest, err := lxd.NewClient(config, destRemote)
	if err != nil {
		return err
	}

	sourceProfs := shared.NewStringSet(status.Profiles)
	destProfs := []string{}

	profiles, err := dest.ListProfiles()
	if err != nil {
		return err
	}

	for _, profile := range profiles {
		destProfs = append(destProfs, profile.Name)
	}

	if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
		return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
	}

	if ephemeral == -1 {
		ct, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		if ct.Ephemeral {
			ephemeral = 1
		} else {
			ephemeral = 0
		}
	}

	sourceWSResponse, err := source.GetMigrationSourceWS(sourceName)
	if err != nil {
		return err
	}

	secrets := map[string]string{}

	op, err := sourceWSResponse.MetadataAsOperation()
	if err != nil {
		return err
	}

	for k, v := range *op.Metadata {
		secrets[k] = v.(string)
	}

	addresses, err := source.Addresses()
	if err != nil {
		return err
	}

	/* Since we're trying a bunch of different network ports that
	 * may be invalid, we can get "bad handshake" errors when the
	 * websocket code tries to connect. If the first error is a
	 * real error, but the subsequent errors are only network
	 * errors, we should try to report the first real error. Of
	 * course, if all the errors are websocket errors, let's just
	 * report that.
	 */
	for _, addr := range addresses {
		var migration *lxd.Response

		sourceWSUrl := "https://" + addr + sourceWSResponse.Operation
		migration, err = dest.MigrateFrom(destName, sourceWSUrl, source.Certificate, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1, false, source, sourceWSResponse.Operation)
		if err != nil {
			continue
		}

		migMap, err := migration.MetadataAsMap()
		if err != nil {
			return err
		}

		if v, ok := (*migMap)["mode"]; ok && v == "pull" {
			if err = dest.WaitForSuccess(migration.Operation); err != nil {
				return err
			}
		}

		if destResource == "" {
			op, err := migration.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	return err
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	if len(args) < 2 {
		return c.copyContainer(config, args[0], "", false, ephem)
	}

	return c.copyContainer(config, args[0], args[1], false, ephem)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type copyCmd struct {
	profArgs      profileList
	confArgs      configList
	ephem         bool
	containerOnly bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--container-only]

Copy containers within or in between LXD instances.`)
}

func (c *copyCmd) flags() {
	gnuflag.Var(&c.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.profArgs, "profile", i18n.G("Profile to apply to the new container"))
	gnuflag.Var(&c.profArgs, "p", i18n.G("Profile to apply to the new container"))
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.containerOnly, "container-only", false, i18n.G("Copy the container without its snapshots"))
}

func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int, stateful bool, containerOnly bool) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" && destResource != "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	var status struct {
		Architecture string
		Devices      map[string]map[string]string
		Config       map[string]string
		Profiles     []string
	}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		result, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles

	} else {
		result, err := source.SnapshotInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles
	}

	if c.profArgs != nil {
		status.Profiles = append(status.Profiles, c.profArgs...)
	}

	if configMap != nil {
		for key, value := range configMap {
			status.Config[key] = value
		}
	}

	baseImage = status.Config["volatile.base_image"]

	if !keepVolatile {
		for k := range status.Config {
			if strings.HasPrefix(k, "volatile") {
				delete(status.Config, k)
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1, containerOnly)
		if err != nil {
			return err
		}

		err = source.WaitForSuccess(cp.Operation)
		if err != nil {
			return err
		}

		if destResource == "" {
			op, err := cp.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	dest, err := lxd.NewClient(config, destRemote)
	if err != nil {
		return err
	}

	sourceProfs := shared.NewStringSet(status.Profiles)
	destProfs := []string{}

	profiles, err := dest.ListProfiles()
	if err != nil {
		return err
	}

	for _, profile := range profiles {
		destProfs = append(destProfs, profile.Name)
	}

	if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
		return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
	}

	if ephemeral == -1 {
		ct, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		if ct.Ephemeral {
			ephemeral = 1
		} else {
			ephemeral = 0
		}
	}

	sourceWSResponse, err := source.GetMigrationSourceWS(sourceName, stateful, containerOnly)
	if err != nil {
		return err
	}

	secrets := map[string]string{}

	op, err := sourceWSResponse.MetadataAsOperation()
	if err != nil {
		return err
	}

	for k, v := range op.Metadata {
		secrets[k] = v.(string)
	}

	addresses, err := source.Addresses()
	if err != nil {
		return err
	}

	/* Since we're trying a bunch of different network ports that
	 * may be invalid, we can get "bad handshake" errors when the
	 * websocket code tries to connect. If the first error is a
	 * real error, but the subsequent errors are only network
	 * errors, we should try to report the first real error. Of
	 * course, if all the errors are websocket errors, let's just
	 * report that.
	 */
	for _, addr := range addresses {
		var migration *api.Response

		sourceWSUrl := "https://" + addr + sourceWSResponse.Operation
		migration, err = dest.MigrateFrom(destName, sourceWSUrl, source.Certificate, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1, false, source, sourceWSResponse.Operation, containerOnly)
		if err != nil {
			continue
		}

		// If push mode is implemented then MigrateFrom will return a
		// non-waitable operation. So this needs to be conditionalized
		// on pull mode.
		if err = dest.WaitForSuccess(migration.Operation); err != nil {
			continue
		}

		if err = source.WaitForSuccess(sourceWSResponse.Operation); err != nil {
			return err
		}

		if destResource == "" {
			op, err := migration.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	// Check for an error at the source
	sourceOp, sourceErr := source.GetOperation(sourceWSResponse.Operation)
	if sourceErr == nil && sourceOp.Err != "" {
		return fmt.Errorf(i18n.G("Migration failed on source host: %s"), sourceOp.Err)
	}

	// Return the error from destination
	return fmt.Errorf(i18n.G("Migration failed on target host: %s"), err)
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	if len(args) < 2 {
		return c.copyContainer(config, args[0], "", false, ephem, false, c.containerOnly)
	}

	return c.copyContainer(config, args[0], args[1], false, ephem, false, c.containerOnly)
}
<commit_msg>copy: wait asynchronously<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type copyCmd struct {
	profArgs      profileList
	confArgs      configList
	ephem         bool
	containerOnly bool
}

func (c *copyCmd) showByDefault() bool {
	return true
}

func (c *copyCmd) usage() string {
	return i18n.G(
		`Usage: lxc copy [<remote>:]<source>[/<snapshot>] [[<remote>:]<destination>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...] [--container-only]

Copy containers within or in between LXD instances.`)
}

func (c *copyCmd) flags() {
	gnuflag.Var(&c.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
	gnuflag.Var(&c.profArgs, "profile", i18n.G("Profile to apply to the new container"))
	gnuflag.Var(&c.profArgs, "p", i18n.G("Profile to apply to the new container"))
	gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
	gnuflag.BoolVar(&c.containerOnly, "container-only", false, i18n.G("Copy the container without its snapshots"))
}

func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int, stateful bool, containerOnly bool) error {
	sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
	destRemote, destName := config.ParseRemoteAndContainer(destResource)

	if sourceName == "" {
		return fmt.Errorf(i18n.G("you must specify a source container name"))
	}

	if destName == "" && destResource != "" {
		destName = sourceName
	}

	source, err := lxd.NewClient(config, sourceRemote)
	if err != nil {
		return err
	}

	var status struct {
		Architecture string
		Devices      map[string]map[string]string
		Config       map[string]string
		Profiles     []string
	}

	// TODO: presumably we want to do this for copying snapshots too? We
	// need to think a bit more about how we track the baseImage in the
	// face of LVM and snapshots in general; this will probably make more
	// sense once that work is done.
	baseImage := ""

	if !shared.IsSnapshot(sourceName) {
		result, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles

	} else {
		result, err := source.SnapshotInfo(sourceName)
		if err != nil {
			return err
		}

		status.Architecture = result.Architecture
		status.Devices = result.Devices
		status.Config = result.Config
		status.Profiles = result.Profiles
	}

	if c.profArgs != nil {
		status.Profiles = append(status.Profiles, c.profArgs...)
	}

	if configMap != nil {
		for key, value := range configMap {
			status.Config[key] = value
		}
	}

	baseImage = status.Config["volatile.base_image"]

	if !keepVolatile {
		for k := range status.Config {
			if strings.HasPrefix(k, "volatile") {
				delete(status.Config, k)
			}
		}
	}

	// Do a local copy if the remotes are the same, otherwise do a migration
	if sourceRemote == destRemote {
		if sourceName == destName {
			return fmt.Errorf(i18n.G("can't copy to the same container name"))
		}

		cp, err := source.LocalCopy(sourceName, destName, status.Config, status.Profiles, ephemeral == 1, containerOnly)
		if err != nil {
			return err
		}

		err = source.WaitForSuccess(cp.Operation)
		if err != nil {
			return err
		}

		if destResource == "" {
			op, err := cp.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	dest, err := lxd.NewClient(config, destRemote)
	if err != nil {
		return err
	}

	sourceProfs := shared.NewStringSet(status.Profiles)
	destProfs := []string{}

	profiles, err := dest.ListProfiles()
	if err != nil {
		return err
	}

	for _, profile := range profiles {
		destProfs = append(destProfs, profile.Name)
	}

	if !sourceProfs.IsSubset(shared.NewStringSet(destProfs)) {
		return fmt.Errorf(i18n.G("not all the profiles from the source exist on the target"))
	}

	if ephemeral == -1 {
		ct, err := source.ContainerInfo(sourceName)
		if err != nil {
			return err
		}

		if ct.Ephemeral {
			ephemeral = 1
		} else {
			ephemeral = 0
		}
	}

	sourceWSResponse, err := source.GetMigrationSourceWS(sourceName, stateful, containerOnly)
	if err != nil {
		return err
	}

	secrets := map[string]string{}

	op, err := sourceWSResponse.MetadataAsOperation()
	if err != nil {
		return err
	}

	for k, v := range op.Metadata {
		secrets[k] = v.(string)
	}

	addresses, err := source.Addresses()
	if err != nil {
		return err
	}

	/* Since we're trying a bunch of different network ports that
	 * may be invalid, we can get "bad handshake" errors when the
	 * websocket code tries to connect. If the first error is a
	 * real error, but the subsequent errors are only network
	 * errors, we should try to report the first real error. Of
	 * course, if all the errors are websocket errors, let's just
	 * report that.
	 */
	waitchan := make(chan map[int]error, 2)
	wait := func(cli *lxd.Client, op string, ch chan map[int]error, senderid int) {
		msg := make(map[int]error, 1)
		err := cli.WaitForSuccess(op)
		if err != nil {
			msg[senderid] = err
			ch <- msg
		}

		msg[senderid] = nil
		ch <- msg
	}
	for _, addr := range addresses {
		var migration *api.Response

		sourceWSUrl := "https://" + addr + sourceWSResponse.Operation
		migration, err = dest.MigrateFrom(destName, sourceWSUrl, source.Certificate, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1, false, source, sourceWSResponse.Operation, containerOnly)
		if err != nil {
			continue
		}

		// If push mode is implemented then MigrateFrom will return a
		// non-waitable operation. So this needs to be conditionalized
		// on pull mode.
		destOpId := 0
		go wait(dest, migration.Operation, waitchan, destOpId)
		sourceOpId := 1
		go wait(source, sourceWSResponse.Operation, waitchan, sourceOpId)

		opStatus := make([]map[int]error, 2)
		for i := 0; i < cap(waitchan); i++ {
			opStatus[i] = <-waitchan
		}

		if opStatus[0][destOpId] != nil {
			continue
		}

		err = opStatus[1][sourceOpId]
		if err != nil {
			return err
		}

		if destResource == "" {
			op, err := migration.MetadataAsOperation()
			if err != nil {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			containers, ok := op.Resources["containers"]
			if !ok || len(containers) == 0 {
				return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
			}

			fields := strings.Split(containers[0], "/")
			fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
		}

		return nil
	}

	// Check for an error at the source
	sourceOp, sourceErr := source.GetOperation(sourceWSResponse.Operation)
	if sourceErr == nil && sourceOp.Err != "" {
		return fmt.Errorf(i18n.G("Migration failed on source host: %s"), sourceOp.Err)
	}

	// Return the error from destination
	return fmt.Errorf(i18n.G("Migration failed on target host: %s"), err)
}

func (c *copyCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	ephem := 0
	if c.ephem {
		ephem = 1
	}

	if len(args) < 2 {
		return c.copyContainer(config, args[0], "", false, ephem, false, c.containerOnly)
	}

	return c.copyContainer(config, args[0], args[1], false, ephem, false, c.containerOnly)
}
<|endoftext|>


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

package main

import (
	"fmt"
	"sync"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"

	"github.com/steveyen/cbdatasource"
)

// Implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	pf         StreamPartitionFunc
	streams    map[string]Stream
	bds        cbdatasource.BucketDataSource

	m       sync.Mutex
	closed  bool
	lastErr error

	seqs map[uint16]uint64 // To track max seq #'s we received per vbucketId.
	meta map[uint16][]byte // To track metadata blob's per vbucketId.

	numError         uint64
	numUpdate        uint64
	numDelete        uint64
	numSnapshotStart uint64
	numSetMetaData   uint64
	numGetMetaData   uint64
	numRollback      uint64
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID string,
	pf StreamPartitionFunc, streams map[string]Stream) (*DCPFeed, error) {
	vbucketIds, err := ParsePartitionsToVBucketIds(streams)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var authFunc cbdatasource.AuthFunc
	var options *cbdatasource.BucketDataSourceOptions

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		pf:         pf,
		streams:    streams,
	}

	feed.bds, err = cbdatasource.NewBucketDataSource([]string{url},
		poolName, bucketName, bucketUUID,
		vbucketIds, authFunc, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("DCPFeed.Start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return fmt.Errorf("already closed")
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("DCPFeed.Close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Streams() map[string]Stream {
	return t.streams
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	log.Printf("DCPFeed.OnError: %s: %v\n", r.name, err)

	r.m.Lock()
	defer r.m.Unlock()
	r.numError += 1

	r.lastErr = err
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataUpdate: %s: vbucketId: %d, key: %s, seq: %d, req: %v\n",
	// r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()
	r.numUpdate += 1

	r.updateSeqUnlocked(vbucketId, seq)
	return nil
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataDelete: %s: vbucketId: %d, key: %s, seq: %d, req: %#v",
	// r.name, vbucketId, key, seq, req)

	r.m.Lock()
	defer r.m.Unlock()
	r.numDelete += 1

	r.updateSeqUnlocked(vbucketId, seq)
	return nil
}

func (r *DCPFeed) updateSeqUnlocked(vbucketId uint16, seq uint64) {
	if r.seqs == nil {
		r.seqs = make(map[uint16]uint64)
	}
	if r.seqs[vbucketId] < seq {
		r.seqs[vbucketId] = seq // Remember the max seq for GetMetaData().
	}
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	log.Printf("DCPFeed.SnapshotStart: %s: vbucketId: %d,"+
		" snapStart: %d, snapEnd: %d, snapType: %d",
		r.name, vbucketId, snapStart, snapEnd, snapType)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSnapshotStart += 1

	return nil
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	log.Printf("DCPFeed.SetMetaData: %s: vbucketId: %d,"+
		" value: %s", r.name, vbucketId, value)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSetMetaData += 1

	if r.meta == nil {
		r.meta = make(map[uint16][]byte)
	}
	r.meta[vbucketId] = value

	return nil
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (value []byte, lastSeq uint64, err error) {
	log.Printf("DCPFeed.GetMetaData: %s: vbucketId: %d", r.name, vbucketId)

	r.m.Lock()
	defer r.m.Unlock()
	r.numGetMetaData += 1

	rv := []byte(nil)
	if r.meta != nil {
		rv = r.meta[vbucketId]
	}

	if r.seqs != nil {
		lastSeq = r.seqs[vbucketId]
	}

	return rv, lastSeq, nil
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	log.Printf("DCPFeed.Rollback: %s: vbucketId: %d,"+
		" rollbackSeq: %d", r.name, vbucketId, rollbackSeq)

	r.m.Lock()
	defer r.m.Unlock()
	r.numRollback += 1

	return fmt.Errorf("bad-rollback")
}
<commit_msg>hookup DCP update/delete<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

import (
	"fmt"
	"sync"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"

	"github.com/steveyen/cbdatasource"
)

// Implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	pf         StreamPartitionFunc
	streams    map[string]Stream
	bds        cbdatasource.BucketDataSource

	m       sync.Mutex
	closed  bool
	lastErr error

	seqs map[uint16]uint64 // To track max seq #'s we received per vbucketId.
	meta map[uint16][]byte // To track metadata blob's per vbucketId.

	numError         uint64
	numUpdate        uint64
	numDelete        uint64
	numSnapshotStart uint64
	numSetMetaData   uint64
	numGetMetaData   uint64
	numRollback      uint64
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID string,
	pf StreamPartitionFunc, streams map[string]Stream) (*DCPFeed, error) {
	vbucketIds, err := ParsePartitionsToVBucketIds(streams)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var authFunc cbdatasource.AuthFunc
	var options *cbdatasource.BucketDataSourceOptions

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		pf:         pf,
		streams:    streams,
	}

	feed.bds, err = cbdatasource.NewBucketDataSource([]string{url},
		poolName, bucketName, bucketUUID,
		vbucketIds, authFunc, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("DCPFeed.Start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return fmt.Errorf("already closed")
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("DCPFeed.Close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Streams() map[string]Stream {
	return t.streams
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	log.Printf("DCPFeed.OnError: %s: %v\n", r.name, err)

	r.m.Lock()
	defer r.m.Unlock()
	r.numError += 1

	r.lastErr = err
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataUpdate: %s: vbucketId: %d, key: %s, seq: %d, req: %v\n",
	// r.name, vbucketId, key, seq, req)

	partition := fmt.Sprintf("%d", vbucketId)
	stream, err := r.pf(req.Key, partition, r.streams)
	if err != nil {
		return fmt.Errorf("error: DCPFeed: partition func error from url: %s,"+
			" poolName: %s, bucketName: %s, req: %#v, streams: %#v, err: %v",
			r.url, r.poolName, r.bucketName, req, r.streams, err)
	}

	r.m.Lock()
	r.numUpdate += 1
	r.updateSeqUnlocked(vbucketId, seq)
	r.m.Unlock()

	stream <- &StreamRequest{
		Op:  STREAM_OP_UPDATE,
		Key: req.Key,
		Val: req.Body,
	}

	return nil
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	// log.Printf("DCPFeed.DataDelete: %s: vbucketId: %d, key: %s, seq: %d, req: %#v",
	// r.name, vbucketId, key, seq, req)

	partition := fmt.Sprintf("%d", vbucketId)
	stream, err := r.pf(req.Key, partition, r.streams)
	if err != nil {
		return fmt.Errorf("error: DCPFeed: partition func error from url: %s,"+
			" poolName: %s, bucketName: %s, req: %#v, streams: %#v, err: %v",
			r.url, r.poolName, r.bucketName, req, r.streams, err)
	}

	r.m.Lock()
	r.numDelete += 1
	r.updateSeqUnlocked(vbucketId, seq)
	r.m.Unlock()

	stream <- &StreamRequest{
		Op:  STREAM_OP_DELETE,
		Key: req.Key,
	}

	return nil
}

func (r *DCPFeed) updateSeqUnlocked(vbucketId uint16, seq uint64) {
	if r.seqs == nil {
		r.seqs = make(map[uint16]uint64)
	}
	if r.seqs[vbucketId] < seq {
		r.seqs[vbucketId] = seq // Remember the max seq for GetMetaData().
	}
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	log.Printf("DCPFeed.SnapshotStart: %s: vbucketId: %d,"+
		" snapStart: %d, snapEnd: %d, snapType: %d",
		r.name, vbucketId, snapStart, snapEnd, snapType)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSnapshotStart += 1

	return nil
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	log.Printf("DCPFeed.SetMetaData: %s: vbucketId: %d,"+
		" value: %s", r.name, vbucketId, value)

	r.m.Lock()
	defer r.m.Unlock()
	r.numSetMetaData += 1

	if r.meta == nil {
		r.meta = make(map[uint16][]byte)
	}
	r.meta[vbucketId] = value

	return nil
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (value []byte, lastSeq uint64, err error) {
	log.Printf("DCPFeed.GetMetaData: %s: vbucketId: %d", r.name, vbucketId)

	r.m.Lock()
	defer r.m.Unlock()
	r.numGetMetaData += 1

	rv := []byte(nil)
	if r.meta != nil {
		rv = r.meta[vbucketId]
	}

	if r.seqs != nil {
		lastSeq = r.seqs[vbucketId]
	}

	return rv, lastSeq, nil
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	log.Printf("DCPFeed.Rollback: %s: vbucketId: %d,"+
		" rollbackSeq: %d", r.name, vbucketId, rollbackSeq)

	r.m.Lock()
	defer r.m.Unlock()
	r.numRollback += 1

	return fmt.Errorf("bad-rollback")
}
<|endoftext|>


<commit_before>/*
* @Author: mustafa
* @Date:   2016-03-29 17:31:09
* @Last Modified by:   mstg
* @Last Modified time: 2016-03-30 05:32:57
*/

package main

import (
  "os"
  "log"
  "debug/macho"
  "github.com/mstg/machotbd/modules"
  "errors"
  "strings"
  "encoding/binary"
  "bytes"
  "fmt"
  "sort"
  "flag"
)

const (
  arm64 macho.Cpu = 16777228

  // From <mach-o/nlist.h>
  N_TYPE uint8 = 0x0e
  N_SECT uint8 = 0xe
  N_EXT uint8 = 0x01
  LoadDylibIdCmd = 0xd
  fileHeaderSize32 = 7 * 4
  fileHeaderSize64 = 8 * 4
  ReExportDylibCmd = (0x1f | 0x80000000)
)

type DylibIdCmd_ struct {
  Cmd macho.LoadCmd
  Len uint32
  Name uint32
  Time uint32
  CurrentVersion uint32
  CompatVersion uint32
}

type ByLength []string

func (s ByLength) Len() int {
  return len(s)
}
func (s ByLength) Swap(i, j int) {
  s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
  return len(s[i]) > len(s[j])
}

func cstring(b []byte) string {
  var i int
  for i = 0; i < len(b) && b[i] != 0; i++ {
  }
  return string(b[0:i])
}

func ver(raw_ver uint32) string {
  return fmt.Sprintf("%d.%d.%d", raw_ver >> 16, (raw_ver >> 8) & 0xff, raw_ver & 0xff)
}

func magic_type(magic uint32) (uint32) {
  if magic == macho.Magic32 {
    return 32
  } else if magic == macho.Magic64 {
    return 64
  } else if magic == macho.MagicFat {
    return 1
  }

  return 0
}

func cpu_type(cpu macho.Cpu) (string) {
  if cpu == macho.CpuAmd64 {
    return "x86_64"
  } else if cpu == macho.CpuArm {
    return "armv7"
  } else if cpu == arm64 {
    return "arm64"
  }

  return "uns"
}

func parse_macho(f *macho.File, stdout *log.Logger, stderr *log.Logger) (tbd.Arch, []string, error) {
  mt := magic_type(f.Magic)
  cput := cpu_type(f.Cpu)

  if cput == "armv7" && f.SubCpu == 11 {
    cput = "armv7s"
  }

  var _syms tbd.Arch

  if cput == "uns" {
    return _syms, []string{}, errors.New("Unsupported arch")
  }

  stdout.Println(mt, "bit", cput, "slice")

  symtab := f.Symtab
  real_symbols := []string{}
  real_classes := []string{}
  real_ivars := []string{}
  real_weak := []string{}
  for _, v := range symtab.Syms {
    if v.Type & N_TYPE == N_SECT && v.Type & N_EXT == N_EXT {
      if v.Name != "" {
        if strings.Contains(v.Name, "_OBJC_CLASS") {
          real_name := strings.Replace(v.Name, "_OBJC_CLASS_$_", "", -1)
          real_classes = append(real_classes, real_name)
        } else if strings.Contains(v.Name, "_OBJC_IVAR") {
          real_name := strings.Replace(v.Name, "_OBJC_IVAR_$_", "", -1)
          real_ivars = append(real_ivars, real_name)
        } else if strings.Contains(v.Name, "_OBJC_METACLASS") {
        } else {
          // Sort weak and strong symbols
          if v.Type & N_SECT == N_SECT {
            real_symbols = append(real_symbols, v.Name)
          } else if v.Type & N_SECT != N_SECT {
            real_weak = append(real_weak, v.Name)
          }
        }
      }
    }
  }

  version := "275.0"
  compatibility_version := ""
  path := ""
  real_reexports := []string{}

  bo := f.ByteOrder
  offset := int64(fileHeaderSize32)
  if f.Magic == macho.Magic64 {
    offset = fileHeaderSize64
  }
  for _, v := range f.Loads {
    dat := v.Raw()
    cmd, siz := uint32(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
    var cmddat []byte
    cmddat, dat = dat[0:siz], dat[siz:]
    offset += int64(siz)

    switch cmd {
    case LoadDylibIdCmd:
      var hdr DylibIdCmd_
      b := bytes.NewReader(cmddat)
      if err := binary.Read(b, bo, &hdr); err != nil {
        break
      }
      path = cstring(cmddat[hdr.Name:])
      version = ver(hdr.CurrentVersion)
      compatibility_version = ver(hdr.CompatVersion)
      break
    case ReExportDylibCmd:
      var hdr DylibIdCmd_
      b := bytes.NewReader(cmddat)
      if err := binary.Read(b, bo, &hdr); err != nil {
        break
      }
      path = cstring(cmddat[hdr.Name:])
      real_reexports = append(real_reexports, path)
      break
    }
  }

  if len(real_reexports) > 0 {
    sort.Sort(ByLength(real_reexports))
  }

  sort.Strings(real_weak)
  sort.Strings(real_symbols)
  sort.Strings(real_classes)
  sort.Strings(real_ivars)

  _syms = tbd.Arch{Name: cput, Symbols: real_symbols, Classes: real_classes, Ivars: real_ivars, Weak: real_weak, ReExports: real_reexports}
  return _syms, []string{version, path, compatibility_version}, nil
}

func parse_fat(f *macho.FatFile, stdout *log.Logger, stderr *log.Logger) (tbd.Tbd_list) {
  stdout.Println("Universal Mach-O")

  _ret_sym := tbd.Tbd_list{}
  for _, v := range f.Arches {
    _ret_macho_sym, info, err := parse_macho(v.File, stdout, stderr)
    if err == nil {
      _ret_sym.Archs = append(_ret_sym.Archs, _ret_macho_sym)
      _ret_sym.Install_name = info[1]
      _ret_sym.Version = info[0]
      _ret_sym.CompVersion = info[2]
    }
  }

  return _ret_sym
}

var out = flag.String("out", "", "path to the file should be exported to")
var print = flag.Bool("print", true, "print tbd to stdout")

func macho_tbd(args []string) {
  stderr := log.New(os.Stderr, "[?] ", 0)
  stdout := log.New(os.Stdout, "[+] ", 0)
  file := ""
  if len(args) > 0 {
    file = args[0]
  } else {
    stderr.Println("No Mach-O file provided")
    os.Exit(1)
  }

  if *out != "" {
    *print = false
  }

  macho_file, err := macho.Open(file)
  var macho_fat_file *macho.FatFile
  universal := false

  if err != nil {
    macho_fat_file, err = macho.OpenFat(file)
  }

  if err != nil {
    stderr.Println("Malformed or invalid Mach-O provided, err:", err)
    os.Exit(1)
  }

  if macho_fat_file != nil {
    universal = true
  }

  var _list tbd.Tbd_list
  if universal {
    _list = parse_fat(macho_fat_file, stdout, stderr)

    stdout.Println("Arch count:", len(_list.Archs))
  } else {
    _unpreplist, info, err := parse_macho(macho_file, stdout, stderr)
    if err == nil {
      arch_arr := []tbd.Arch{_unpreplist}
      _list = tbd.Tbd_list{Archs: arch_arr}
      _list.Install_name = info[1]
      _list.Version = info[0]
      _list.CompVersion = info[2]
    }
  }

  _buf := tbd.Tbd_form(_list)

  printit := 0
  if *print == true && *out == "" {
    println(_buf.String())
  } else if *out != "" {
    _, err := os.Stat(*out)

    if os.IsNotExist(err) {
      var file, err = os.Create(*out)
      if err != nil {
        printit = 1
      } else {
        defer file.Close()
      }
    }

    file, err := os.OpenFile(*out, os.O_RDWR, 0644)

    if err != nil {
      printit = 1
    } else {
      defer file.Close()
    }

    _, err = file.WriteString(_buf.String())

    if err != nil {
      printit = 1
    } else {
      err = file.Sync()
      if err != nil {
        printit = 1
      }
    }
  }

  if printit == 1 {
    stderr.Println("An error occured during I/O, printing to stdout")
    println(_buf.String())
  } else if *out != "" {
    stdout.Println("Wrote to", *out)
  }
}

func main() {
  flag.Parse()
  macho_tbd(flag.Args())
}
<commit_msg>Add armv6 support<commit_after>/*
* @Author: mustafa
* @Date:   2016-03-29 17:31:09
* @Last Modified by:   mstg
* @Last Modified time: 2016-03-30 05:32:57
*/

package main

import (
  "os"
  "log"
  "debug/macho"
  "github.com/mstg/machotbd/modules"
  "errors"
  "strings"
  "encoding/binary"
  "bytes"
  "fmt"
  "sort"
  "flag"
)

const (
  arm64 macho.Cpu = 16777228

  // From <mach-o/nlist.h>
  N_TYPE uint8 = 0x0e
  N_SECT uint8 = 0xe
  N_EXT uint8 = 0x01
  LoadDylibIdCmd = 0xd
  fileHeaderSize32 = 7 * 4
  fileHeaderSize64 = 8 * 4
  ReExportDylibCmd = (0x1f | 0x80000000)
)

type DylibIdCmd_ struct {
  Cmd macho.LoadCmd
  Len uint32
  Name uint32
  Time uint32
  CurrentVersion uint32
  CompatVersion uint32
}

type ByLength []string

func (s ByLength) Len() int {
  return len(s)
}
func (s ByLength) Swap(i, j int) {
  s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
  return len(s[i]) > len(s[j])
}

func cstring(b []byte) string {
  var i int
  for i = 0; i < len(b) && b[i] != 0; i++ {
  }
  return string(b[0:i])
}

func ver(raw_ver uint32) string {
  return fmt.Sprintf("%d.%d.%d", raw_ver >> 16, (raw_ver >> 8) & 0xff, raw_ver & 0xff)
}

func magic_type(magic uint32) (uint32) {
  if magic == macho.Magic32 {
    return 32
  } else if magic == macho.Magic64 {
    return 64
  } else if magic == macho.MagicFat {
    return 1
  }

  return 0
}

func cpu_type(f *macho.File) (string) {
  if f.Cpu == macho.CpuAmd64 {
    return "x86_64"
  } else if f.Cpu == macho.CpuArm && f.SubCpu == 6 {
    return "armv6"
  } else if f.Cpu == macho.CpuArm && f.SubCpu == 9 {
    return "armv7"
  } else if f.Cpu == macho.CpuArm && f.SubCpu == 11 {
    return "armv7s"
  } else if f.Cpu == arm64 {
    return "arm64"
  }

  println(f.SubCpu)

  return "uns"
}

func parse_macho(f *macho.File, stdout *log.Logger, stderr *log.Logger) (tbd.Arch, []string, error) {
  mt := magic_type(f.Magic)
  cput := cpu_type(f)

  if cput == "armv7" && f.SubCpu == 11 {
    cput = "armv7s"
  }

  var _syms tbd.Arch

  if cput == "uns" {
    return _syms, []string{}, errors.New("Unsupported arch")
  }

  stdout.Println(mt, "bit", cput, "slice")

  symtab := f.Symtab
  real_symbols := []string{}
  real_classes := []string{}
  real_ivars := []string{}
  real_weak := []string{}
  for _, v := range symtab.Syms {
    if v.Type & N_TYPE == N_SECT && v.Type & N_EXT == N_EXT {
      if v.Name != "" {
        if strings.Contains(v.Name, "_OBJC_CLASS") {
          real_name := strings.Replace(v.Name, "_OBJC_CLASS_$_", "", -1)
          real_classes = append(real_classes, real_name)
        } else if strings.Contains(v.Name, "_OBJC_IVAR") {
          real_name := strings.Replace(v.Name, "_OBJC_IVAR_$_", "", -1)
          real_ivars = append(real_ivars, real_name)
        } else if strings.Contains(v.Name, "_OBJC_METACLASS") {
        } else {
          // Sort weak and strong symbols
          if v.Type & N_SECT == N_SECT {
            real_symbols = append(real_symbols, v.Name)
          }/* else if v.Type & N_SECT != N_SECT {
            real_weak = append(real_weak, v.Name)
          }*/ // Disable weak symbol finding until I find a better solution
        }
      }
    }
  }

  version := "275.0"
  compatibility_version := ""
  path := ""
  real_reexports := []string{}

  bo := f.ByteOrder
  offset := int64(fileHeaderSize32)
  if f.Magic == macho.Magic64 {
    offset = fileHeaderSize64
  }
  for _, v := range f.Loads {
    dat := v.Raw()
    cmd, siz := uint32(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
    var cmddat []byte
    cmddat, dat = dat[0:siz], dat[siz:]
    offset += int64(siz)

    switch cmd {
    case LoadDylibIdCmd:
      var hdr DylibIdCmd_
      b := bytes.NewReader(cmddat)
      if err := binary.Read(b, bo, &hdr); err != nil {
        break
      }
      path = cstring(cmddat[hdr.Name:])
      version = ver(hdr.CurrentVersion)
      compatibility_version = ver(hdr.CompatVersion)
      break
    case ReExportDylibCmd:
      var hdr DylibIdCmd_
      b := bytes.NewReader(cmddat)
      if err := binary.Read(b, bo, &hdr); err != nil {
        break
      }
      path = cstring(cmddat[hdr.Name:])
      real_reexports = append(real_reexports, path)
      break
    }
  }

  if len(real_reexports) > 0 {
    sort.Sort(ByLength(real_reexports))
  }

  sort.Strings(real_weak)
  sort.Strings(real_symbols)
  sort.Strings(real_classes)
  sort.Strings(real_ivars)

  _syms = tbd.Arch{Name: cput, Symbols: real_symbols, Classes: real_classes, Ivars: real_ivars, Weak: real_weak, ReExports: real_reexports}
  return _syms, []string{version, path, compatibility_version}, nil
}

func parse_fat(f *macho.FatFile, stdout *log.Logger, stderr *log.Logger) (tbd.Tbd_list) {
  stdout.Println("Universal Mach-O")

  _ret_sym := tbd.Tbd_list{}
  for _, v := range f.Arches {
    _ret_macho_sym, info, err := parse_macho(v.File, stdout, stderr)
    if err == nil {
      _ret_sym.Archs = append(_ret_sym.Archs, _ret_macho_sym)
      _ret_sym.Install_name = info[1]
      _ret_sym.Version = info[0]
      _ret_sym.CompVersion = info[2]
    }
  }

  return _ret_sym
}

var out = flag.String("out", "", "path to the file should be exported to")
var print = flag.Bool("print", true, "print tbd to stdout")

func macho_tbd(args []string) {
  stderr := log.New(os.Stderr, "[?] ", 0)
  stdout := log.New(os.Stdout, "[+] ", 0)
  file := ""
  if len(args) > 0 {
    file = args[0]
  } else {
    stderr.Println("No Mach-O file provided")
    os.Exit(1)
  }

  if *out != "" {
    *print = false
  }

  macho_file, err := macho.Open(file)
  var macho_fat_file *macho.FatFile
  universal := false

  if err != nil {
    macho_fat_file, err = macho.OpenFat(file)
  }

  if err != nil {
    stderr.Println("Malformed or invalid Mach-O provided, err:", err)
    os.Exit(1)
  }

  if macho_fat_file != nil {
    universal = true
  }

  var _list tbd.Tbd_list
  if universal {
    _list = parse_fat(macho_fat_file, stdout, stderr)

    stdout.Println("Arch count:", len(_list.Archs))
  } else {
    _unpreplist, info, err := parse_macho(macho_file, stdout, stderr)
    if err == nil {
      arch_arr := []tbd.Arch{_unpreplist}
      _list = tbd.Tbd_list{Archs: arch_arr}
      _list.Install_name = info[1]
      _list.Version = info[0]
      _list.CompVersion = info[2]
    }
  }

  _buf := tbd.Tbd_form(_list)

  printit := 0
  if *print == true && *out == "" {
    println(_buf.String())
  } else if *out != "" {
    _, err := os.Stat(*out)

    if os.IsNotExist(err) {
      var file, err = os.Create(*out)
      if err != nil {
        printit = 1
      } else {
        defer file.Close()
      }
    }

    file, err := os.OpenFile(*out, os.O_RDWR, 0644)

    if err != nil {
      printit = 1
    } else {
      defer file.Close()
    }

    _, err = file.WriteString(_buf.String())

    if err != nil {
      printit = 1
    } else {
      err = file.Sync()
      if err != nil {
        printit = 1
      }
    }
  }

  if printit == 1 {
    stderr.Println("An error occured during I/O, printing to stdout")
    println(_buf.String())
  } else if *out != "" {
    stdout.Println("Wrote to", *out)
  }
}

func main() {
  flag.Parse()
  macho_tbd(flag.Args())
}
<|endoftext|>


<commit_before>package fastq

import (
	"errors"
	"sync"
)

var (
	ErrEmptyInputFile  = errors.New("No Input Fastq File Given")
	ErrUnPairInputFile = errors.New("Input Fastq File Not Paired")
)

type Pair struct {
	Read1 *Fastq
	Read2 *Fastq
}

func (p Pair) String() string {
	return fmt.Sprintf("%s\n%s", p.Read1, p.Read2)
}

type FastqPairFile struct {
<<<<<<< HEAD
	Name1 string
	Name2 string
	file1 *FastqFile
	file2 *FastqFile
=======
	ff1 *FastqFile
	ff2 *FastqFile
	err error
}

func (pf *FastqPairFile) Filenames() (string, string) {
	return pf.ff1.Name, pf.ff2.Name
}

func (pf *FastqPairFile) Err() error {
	if err := pf.ff1.Err(); err != nil {
		return err
	} else if err := pf.ff2.Err(); err != nil {
		return err
	}
	return pf.err
>>>>>>> align
}

func (pf *FastqPairFile) Close() error {
	if err := pf.ff1.Close(); err != nil {
		return err
	}
	if err := pf.ff2.Close(); err != nil {
		return err
	}
	return nil
}

func (pf *FastqPairFile) Next() bool {
	if pf.ff1.Next() && pf.ff2.Next() {
		return true
	}
	return false
}

func (pf *FastqPairFile) Value() *Pair {
	return &Pair{
		Read1: pf.ff1.Value(),
		Read2: pf.ff2.Value(),
	}
}

func (pf *FastqPairFile) Iter() <-chan *Pair {
	out := make(chan *Pair)
	go func(pf *FastqPairFile, out chan *Pair) {
		for pf.Next() {
			out <- pf.Value()
		}
		close(out)
	}(pf, out)
	return out
}

func OpenPair(filename1, filename2 string) (*FastqPairFile, error) {
	ff1, err := Open(filename1)
	if err != nil {
		return nil, err
	}
	ff2, err := Open(filename2)
	if err != nil {
		return nil, err
	}
	return &FastqPairFile{
<<<<<<< HEAD
		Name1: filename1,
		Name2: filename2,
		file1: file1,
		file2: file2,
=======
		ff1: ff1,
		ff2: ff2,
>>>>>>> align
	}, nil
}

func LoadPair(filename1, filename2 string) (<-chan *Pair, error) {
	pf, err := OpenPair(filename1, filename2)
	if err != nil {
		return nil, err
	}
	return pf.Iter(), nil
}

func OpenPairs(filenames ...string) ([]*FastqPairFile, error) {
	if l := len(filenames); l == 0 {
		return nil, ErrEmptyInputFile
	} else if l%2 != 0 {
		return nil, ErrUnPairInputFile
	}
	pfs := make([]*FastqPairFile, len(filenames)/2)
	for i := 0; i < len(filenames); i += 2 {
		filename1 := filenames[i]
		filename2 := filenames[i+1]
		pf, err := OpenPair(filename1, filename2)
		if err != nil {
			return nil, err
		}
		pfs[i/2] = pf
	}
	return pfs, nil
}

func Iter(filenames ...string) (<-chan *Pair, error) {
	pfs, err := OpenPairs(filenames...)
	if err != nil {
		return nil, err
	}

	ch := make(chan *Pair, 4*len(pfs))
	go func(ch chan *Pair, pfs []*FastqPairFile) {
		wg := &sync.WaitGroup{}
		wg.Add(len(pfs))
		for _, pf := range pfs {
			go func(ch chan *Pair, pf *FastqPairFile, wg *sync.WaitGroup) {
				defer pf.Close()
				for pf.Next() {
					ch <- pf.Value()
				}
				wg.Done()
			}(ch, pf, wg)
		}
		wg.Wait()
		close(ch)
	}(ch, pfs)
	return ch, nil
}
<commit_msg>update<commit_after>package fastq

import (
	"errors"
	"fmt"
	"sync"
)

var (
	ErrEmptyInputFile  = errors.New("No Input Fastq File Given")
	ErrUnPairInputFile = errors.New("Input Fastq File Not Paired")
)

type Pair struct {
	Read1 *Fastq
	Read2 *Fastq
}

func (p Pair) String() string {
	return fmt.Sprintf("%s\n%s", p.Read1, p.Read2)
}

type FastqPairFile struct {
	ff1 *FastqFile
	ff2 *FastqFile
	err error
}

func (pf *FastqPairFile) Filenames() (string, string) {
	return pf.ff1.Name, pf.ff2.Name
}

func (pf *FastqPairFile) Err() error {
	if err := pf.ff1.Err(); err != nil {
		return err
	} else if err := pf.ff2.Err(); err != nil {
		return err
	}
	return pf.err
}

func (pf *FastqPairFile) Close() error {
	if err := pf.ff1.Close(); err != nil {
		return err
	}
	if err := pf.ff2.Close(); err != nil {
		return err
	}
	return nil
}

func (pf *FastqPairFile) Next() bool {
	if pf.ff1.Next() && pf.ff2.Next() {
		return true
	}
	return false
}

func (pf *FastqPairFile) Value() *Pair {
	return &Pair{
		Read1: pf.ff1.Value(),
		Read2: pf.ff2.Value(),
	}
}

func (pf *FastqPairFile) Iter() <-chan *Pair {
	out := make(chan *Pair)
	go func(pf *FastqPairFile, out chan *Pair) {
		for pf.Next() {
			out <- pf.Value()
		}
		close(out)
	}(pf, out)
	return out
}

func OpenPair(filename1, filename2 string) (*FastqPairFile, error) {
	ff1, err := Open(filename1)
	if err != nil {
		return nil, err
	}
	ff2, err := Open(filename2)
	if err != nil {
		return nil, err
	}
	return &FastqPairFile{
		ff1: ff1,
		ff2: ff2,
	}, nil
}

func LoadPair(filename1, filename2 string) (<-chan *Pair, error) {
	pf, err := OpenPair(filename1, filename2)
	if err != nil {
		return nil, err
	}
	return pf.Iter(), nil
}

func OpenPairs(filenames ...string) ([]*FastqPairFile, error) {
	if l := len(filenames); l == 0 {
		return nil, ErrEmptyInputFile
	} else if l%2 != 0 {
		return nil, ErrUnPairInputFile
	}
	pfs := make([]*FastqPairFile, len(filenames)/2)
	for i := 0; i < len(filenames); i += 2 {
		filename1 := filenames[i]
		filename2 := filenames[i+1]
		pf, err := OpenPair(filename1, filename2)
		if err != nil {
			return nil, err
		}
		pfs[i/2] = pf
	}
	return pfs, nil
}

func Iter(filenames ...string) (<-chan *Pair, error) {
	pfs, err := OpenPairs(filenames...)
	if err != nil {
		return nil, err
	}

	ch := make(chan *Pair, 4*len(pfs))
	go func(ch chan *Pair, pfs []*FastqPairFile) {
		wg := &sync.WaitGroup{}
		wg.Add(len(pfs))
		for _, pf := range pfs {
			go func(ch chan *Pair, pf *FastqPairFile, wg *sync.WaitGroup) {
				defer pf.Close()
				for pf.Next() {
					ch <- pf.Value()
				}
				wg.Done()
			}(ch, pf, wg)
		}
		wg.Wait()
		close(ch)
	}(ch, pfs)
	return ch, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package sippy_header

import (
    "time"

    "sippy/net"
)

var _sip_date_name normalName = newNormalName("Date")

type SipDate struct {
    normalName
    str_body    string
    ts          time.Time
    parsed      bool
}

func CreateSipDate(body string) []SipHeader {
    return []SipHeader{
        &SipDate{
            normalName  : _sip_date_name,
            str_body    : body,
            parsed      : false,
        },
    }
}

func NewSipDate(ts time.Time) *SipDate {
    return &SipDate{
        normalName  : _sip_date_name,
        str_body    : ts.UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"),
        parsed      : true,
        ts          : ts,
    }
}

func (self *SipDate) GetCopy() *SipDate {
    tmp := *self
    return &tmp
}

func (self *SipDate) GetCopyAsIface() SipHeader {
    return self.GetCopy()
}

func (self *SipDate) LocalStr(hostport *sippy_net.HostPort, compact bool) string {
    return self.String()
}

func (self *SipDate) String() string {
    return self.Name() + ": " + self.str_body
}

func (self *SipDate) StringBody() string {
    return self.str_body
}

func (self *SipDate) GetTime() (time.Time, error) {
    var err error

    if self.parsed {
        return self.ts, nil
    }
    self.ts, err = time.Parse("Mon, 2 Jan 2006 15:04:05 MST", self.str_body)
    if err == nil {
        self.parsed = true
    }
    return self.ts, err
}
<commit_msg>The Date: must be in GMT.<commit_after>// Copyright (c) 2021 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package sippy_header

import (
    "time"

    "sippy/net"
)

var _sip_date_name normalName = newNormalName("Date")

type SipDate struct {
    normalName
    str_body    string
    ts          time.Time
    parsed      bool
}

func CreateSipDate(body string) []SipHeader {
    return []SipHeader{
        &SipDate{
            normalName  : _sip_date_name,
            str_body    : body,
            parsed      : false,
        },
    }
}

func NewSipDate(ts time.Time) *SipDate {
    return &SipDate{
        normalName  : _sip_date_name,
        str_body    : ts.In(time.FixedZone("GMT", 0)).Format("Mon, 2 Jan 2006 15:04:05 MST"),
        parsed      : true,
        ts          : ts,
    }
}

func (self *SipDate) GetCopy() *SipDate {
    tmp := *self
    return &tmp
}

func (self *SipDate) GetCopyAsIface() SipHeader {
    return self.GetCopy()
}

func (self *SipDate) LocalStr(hostport *sippy_net.HostPort, compact bool) string {
    return self.String()
}

func (self *SipDate) String() string {
    return self.Name() + ": " + self.str_body
}

func (self *SipDate) StringBody() string {
    return self.str_body
}

func (self *SipDate) GetTime() (time.Time, error) {
    var err error

    if self.parsed {
        return self.ts, nil
    }
    self.ts, err = time.Parse("Mon, 2 Jan 2006 15:04:05 MST", self.str_body)
    if err == nil {
        self.parsed = true
    }
    return self.ts, err
}
<|endoftext|>


<commit_before>// Package bitutils provides a collection of utilities to deal with bits.
package bitutils

import (
	"fmt"
	"strconv"
)

// W is the length of a machine word.
const W = 64

// Magic constants.
const (
	Lsh2 = 0x5555555555555555
	Lsh4 = 0x3333333333333333
	Lsh8 = 0x0f0f0f0f0f0f0f0f

	Lsb8 = 0x0101010101010101

	Msb2 = 0xaaaaaaaaaaaaaaaa
	Msb8 = 0x8080808080808080
)

// Word represents a 64-bit binary string.
type Word uint64

var (
	Pos  [W + 1]Word // Pos[i] has a 1 only at i.
	PosC [W + 1]Word // PosC[i] has a 0 only at i.
	Lsh  [W + 1]Word // Lsh[i] has 1s in its i LSBs.
	Msh  [W + 1]Word // Msh[i] has 1s in its i MSBs.
)

func init() {
	for i := 0; i < len(Pos); i++ {
		Pos[i] = Word(1) << uint(i)
		PosC[i] = ^Pos[i]
		Lsh[i] = Pos[i] - 1
		Msh[i] = Lsh[i] << uint(W-i)
	}
}

// ParseWord returns a Word from a string.
func ParseWord(s string) (Word, error) {
	w, err := strconv.ParseUint(s, 2, 64)
	return Word(w), err
}

// String returns binary string w[0]w[1]...w[63].
func (w Word) String() string {
	return fmt.Sprintf("%064b", w)
}

// Count1 returns the number of ones contained in w.
func (w Word) Count1() int {
	w -= (w >> 1) & Lsh2
	w = (w & Lsh4) + ((w >> 2) & Lsh4)
	w = (w + (w >> 4)) & Lsh8
	return int((w * Lsb8) >> 56)
}

// Count0 returns the number of zeros contained in w.
func (w Word) Count0() int {
	w = ^w
	return w.Count1()
}

// Count returns the number of b[0]'s contained in w.
func (w Word) Count(b int) int {
	w = w ^ (^Word(0) + Word(b))
	return w.Count1()
}

// Get returns w[i].
func (w Word) Get(i int) Word {
	w = w >> uint(i)
	return w & Pos[0]
}

// Set1 sets w[i] to 1.
func (w Word) Set1(i int) Word {
	return w | Pos[i]
}

// Set0 sets w[i] to 0.
func (w Word) Set0(i int) Word {
	return w & PosC[i]
}

// Flip flips w[i].
func (w Word) Flip(i int) Word {
	return w ^ Pos[i]
}

// Least1 returns a word that indicates the least 1 in w.
func (w Word) Least1() Word {
	if w == 0 {
		return 0
	}
	w = ((w - 1) ^ w) & w
	return w
}

// LeastIndex1 returns the index of the least 1 in w if exists and -1
// otherwise.
func (w Word) LeastIndex1() int {
	if w == 0 {
		return -1
	}
	w = (w - 1) ^ w
	return w.Count1() - 1
}

// Rank1 returns the number of ones in w[0]...w[i].
func (w Word) Rank1(i int) int {
	w = w << uint(W-i-1)
	return w.Count1()
}

// Rank0 returns the number of zeros in w[0]...w[i].
func (w Word) Rank0(i int) int {
	w = ^w << uint(W-i-1)
	return w.Count1()
}

func (w Word) zcmp8() Word {
	w = w | ((w | Msb8) - Lsb8)
	return (w & Msb8) >> 7
}

func (w Word) leq8(v Word) Word {
	w = (((v | Msb8) - (w & ^Word(Lsb8))) ^ w) ^ v
	return (w & Msb8) >> 7
}

// Select1 returns the ith 1 in w.
func (w Word) Select1(i int) int {
	s := w - ((w & Msb2) >> 1)
	s = (s & Lsh4) + ((s >> 2) & Lsh4)
	s = ((s + (s >> 4)) & Lsh8) * Lsb8
	b := ((s.leq8(Word(i)*Lsb8) * Lsb8) >> 53) & ^Word(0x0111)
	l := Word(i) - (((s << 8) >> b) & 0xff)
	s = ((((w >> b) & 0xff) * Lsb8) & 0x8040201008040201).zcmp8() * Lsb8
	if w = b + ((s.leq8(l * Lsb8) * Lsb8) >> 56); w != 0x48 {
		return int(w)
	} else {
		return -1
	}

}
<commit_msg>Add missing constants for symmetry<commit_after>// Package bitutils provides a collection of utilities to deal with bits.
package bitutils

import (
	"fmt"
	"strconv"
)

// W is the length of a machine word.
const W = 64

// Magic constants.
const (
	Lsh2 = 0x5555555555555555
	Lsh4 = 0x3333333333333333
	Lsh8 = 0x0f0f0f0f0f0f0f0f

	Msh2 = 0xaaaaaaaaaaaaaaaa
	Msh4 = 0xcccccccccccccccc
	Msh8 = 0xf0f0f0f0f0f0f0f0

	Lsb2 = 0x5555555555555555
	Lsb4 = 0x1111111111111111
	Lsb8 = 0x0101010101010101

	Msb2 = 0xaaaaaaaaaaaaaaaa
	Msb4 = 0x8888888888888888
	Msb8 = 0x8080808080808080
)

// Word represents a 64-bit binary string.
type Word uint64

var (
	Pos  [W + 1]Word // Pos[i] has a 1 only at i.
	PosC [W + 1]Word // PosC[i] has a 0 only at i.
	Lsh  [W + 1]Word // Lsh[i] has 1s in its i LSBs.
	Msh  [W + 1]Word // Msh[i] has 1s in its i MSBs.
)

func init() {
	for i := 0; i < len(Pos); i++ {
		Pos[i] = Word(1) << uint(i)
		PosC[i] = ^Pos[i]
		Lsh[i] = Pos[i] - 1
		Msh[i] = Lsh[i] << uint(W-i)
	}
}

// ParseWord returns a Word from a string.
func ParseWord(s string) (Word, error) {
	w, err := strconv.ParseUint(s, 2, 64)
	return Word(w), err
}

// String returns binary string w[0]w[1]...w[63].
func (w Word) String() string {
	return fmt.Sprintf("%064b", w)
}

// Count1 returns the number of ones contained in w.
func (w Word) Count1() int {
	w -= (w >> 1) & Lsh2
	w = (w & Lsh4) + ((w >> 2) & Lsh4)
	w = (w + (w >> 4)) & Lsh8
	return int((w * Lsb8) >> 56)
}

// Count0 returns the number of zeros contained in w.
func (w Word) Count0() int {
	w = ^w
	return w.Count1()
}

// Count returns the number of b[0]'s contained in w.
func (w Word) Count(b int) int {
	w = w ^ (^Word(0) + Word(b))
	return w.Count1()
}

// Get returns w[i].
func (w Word) Get(i int) Word {
	w = w >> uint(i)
	return w & Pos[0]
}

// Set1 sets w[i] to 1.
func (w Word) Set1(i int) Word {
	return w | Pos[i]
}

// Set0 sets w[i] to 0.
func (w Word) Set0(i int) Word {
	return w & PosC[i]
}

// Flip flips w[i].
func (w Word) Flip(i int) Word {
	return w ^ Pos[i]
}

// Least1 returns a word that indicates the least 1 in w.
func (w Word) Least1() Word {
	if w == 0 {
		return 0
	}
	w = ((w - 1) ^ w) & w
	return w
}

// LeastIndex1 returns the index of the least 1 in w if exists and -1
// otherwise.
func (w Word) LeastIndex1() int {
	if w == 0 {
		return -1
	}
	w = (w - 1) ^ w
	return w.Count1() - 1
}

// Rank1 returns the number of ones in w[0]...w[i].
func (w Word) Rank1(i int) int {
	w = w << uint(W-i-1)
	return w.Count1()
}

// Rank0 returns the number of zeros in w[0]...w[i].
func (w Word) Rank0(i int) int {
	w = ^w << uint(W-i-1)
	return w.Count1()
}

func (w Word) zcmp8() Word {
	w = w | ((w | Msb8) - Lsb8)
	return (w & Msb8) >> 7
}

func (w Word) leq8(v Word) Word {
	w = (((v | Msb8) - (w & ^Word(Lsb8))) ^ w) ^ v
	return (w & Msb8) >> 7
}

// Select1 returns the ith 1 in w.
func (w Word) Select1(i int) int {
	s := w - ((w & Msb2) >> 1)
	s = (s & Lsh4) + ((s >> 2) & Lsh4)
	s = ((s + (s >> 4)) & Lsh8) * Lsb8
	b := ((s.leq8(Word(i)*Lsb8) * Lsb8) >> 53) & ^Word(0x0111)
	l := Word(i) - (((s << 8) >> b) & 0xff)
	s = ((((w >> b) & 0xff) * Lsb8) & 0x8040201008040201).zcmp8() * Lsb8
	if w = b + ((s.leq8(l * Lsb8) * Lsb8) >> 56); w != 0x48 {
		return int(w)
	} else {
		return -1
	}

}
<|endoftext|>