<commit_before>package stripe

import "encoding/json"

// IssuingCardPINStatus is the list of possible values for the status field of a Card PIN.
type IssuingCardPINStatus string

// List of values that IssuingCardPINStatus can take.
const (
	IssuingCardPINStatusActive  IssuingCardPINStatus = "active"
	IssuingCardPINStatusBlocked IssuingCardPINStatus = "blocked"
)

// IssuingCardReplacementReason is the list of possible values for the replacement reason on an
// issuing card.
type IssuingCardReplacementReason string

// List of values that IssuingCardReplacementReason can take.
const (
	IssuingCardReplacementReasonDamage     IssuingCardReplacementReason = "damage"
	IssuingCardReplacementReasonExpiration IssuingCardReplacementReason = "expiration"
	IssuingCardReplacementReasonLoss       IssuingCardReplacementReason = "loss"
	IssuingCardReplacementReasonTheft      IssuingCardReplacementReason = "theft"
)

// IssuingCardShippingStatus is the list of possible values for the shipping status
// on an issuing card.
type IssuingCardShippingStatus string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingCardShippingTypeDelivered IssuingCardShippingStatus = "delivered"
	IssuingCardShippingTypeFailure   IssuingCardShippingStatus = "failure"
	IssuingCardShippingTypePending   IssuingCardShippingStatus = "pending"
	IssuingCardShippingTypeReturned  IssuingCardShippingStatus = "returned"
	IssuingCardShippingTypeShipped   IssuingCardShippingStatus = "shipped"
)

// IssuingCardShippingService is the shipment service for a card.
type IssuingCardShippingService string

// List of values that IssuingCardShippingService can take.
const (
	IssuingCardShippingServiceExpress  IssuingCardShippingService = "express"
	IssuingCardShippingServicePriority IssuingCardShippingService = "priority"
	IssuingCardShippingServiceStandard IssuingCardShippingService = "standard"

	// The following value is deprecated, use IssuingCardShippingServicePriority instead
	IssuingCardShippingServiceOvernight IssuingCardShippingService = "overnight"
)

// IssuingCardShippingSpeed is the shipment speed for a card.
// This is deprecated, use IssuingCardShippingService instead
type IssuingCardShippingSpeed string

// List of values that IssuingCardShippingSpeed can take
const (
	IssuingCardShippingSpeedExpress   IssuingCardShippingSpeed = "express"
	IssuingCardShippingSpeedOvernight IssuingCardShippingSpeed = "overnight"
	IssuingCardShippingSpeedStandard  IssuingCardShippingSpeed = "standard"
)

// IssuingCardShippingType is the list of possible values for the shipping type
// on an issuing card.
type IssuingCardShippingType string

// List of values that IssuingCardShippingType can take.
const (
	IssuingCardShippingTypeBulk       IssuingCardShippingType = "bulk"
	IssuingCardShippingTypeIndividual IssuingCardShippingType = "individual"
)

// IssuingCardSpendingControlsSpendingLimitInterval is the list of possible values for the interval
// for a spending limit on an issuing card.
type IssuingCardSpendingControlsSpendingLimitInterval string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingCardSpendingControlsSpendingLimitIntervalAllTime          IssuingCardSpendingControlsSpendingLimitInterval = "all_time"
	IssuingCardSpendingControlsSpendingLimitIntervalDaily            IssuingCardSpendingControlsSpendingLimitInterval = "daily"
	IssuingCardSpendingControlsSpendingLimitIntervalMonthly          IssuingCardSpendingControlsSpendingLimitInterval = "monthly"
	IssuingCardSpendingControlsSpendingLimitIntervalPerAuthorization IssuingCardSpendingControlsSpendingLimitInterval = "per_authorization"
	IssuingCardSpendingControlsSpendingLimitIntervalWeekly           IssuingCardSpendingControlsSpendingLimitInterval = "weekly"
	IssuingCardSpendingControlsSpendingLimitIntervalYearly           IssuingCardSpendingControlsSpendingLimitInterval = "yearly"
)

// IssuingCardStatus is the list of possible values for status on an issuing card.
type IssuingCardStatus string

// List of values that IssuingCardStatus can take.
const (
	IssuingCardStatusActive   IssuingCardStatus = "active"
	IssuingCardStatusCanceled IssuingCardStatus = "canceled"
	IssuingCardStatusInactive IssuingCardStatus = "inactive"
	IssuingCardStatusPending  IssuingCardStatus = "pending"
)

// IssuingCardType is the type of an issuing card.
type IssuingCardType string

// List of values that IssuingCardType can take.
const (
	IssuingCardTypePhysical IssuingCardType = "physical"
	IssuingCardTypeVirtual  IssuingCardType = "virtual"
)

// IssuingSpendingLimitInterval is the list of possible values for the interval of a given
// spending limit on an issuing card or cardholder.
// This is deprecated, use IssuingCardSpendingControlsSpendingLimitInterval instead
type IssuingSpendingLimitInterval string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingSpendingLimitIntervalAllTime          IssuingSpendingLimitInterval = "all_time"
	IssuingSpendingLimitIntervalDaily            IssuingSpendingLimitInterval = "daily"
	IssuingSpendingLimitIntervalMonthly          IssuingSpendingLimitInterval = "monthly"
	IssuingSpendingLimitIntervalPerAuthorization IssuingSpendingLimitInterval = "per_authorization"
	IssuingSpendingLimitIntervalWeekly           IssuingSpendingLimitInterval = "weekly"
	IssuingSpendingLimitIntervalYearly           IssuingSpendingLimitInterval = "yearly"
)

// IssuingAuthorizationControlsSpendingLimitsParams is the set of parameters that can be used for
// the spending limits associated with a given issuing card or cardholder.
// This is deprecated and will be removed in the next major version.
type IssuingAuthorizationControlsSpendingLimitsParams struct {
	Amount     *int64    `form:"amount"`
	Categories []*string `form:"categories"`
	Interval   *string   `form:"interval"`
}

// AuthorizationControlsParams is the set of parameters that can be used for the shipping parameter.
// This is deprecated and will be removed in the next major version.
type AuthorizationControlsParams struct {
	AllowedCategories []*string                                           `form:"allowed_categories"`
	BlockedCategories []*string                                           `form:"blocked_categories"`
	MaxApprovals      *int64                                              `form:"max_approvals"`
	SpendingLimits    []*IssuingAuthorizationControlsSpendingLimitsParams `form:"spending_limits"`

	// The following parameter only applies to Cardholder
	SpendingLimitsCurrency *string `form:"spending_limits_currency"`

	// The following parameter is deprecated
	MaxAmount *int64 `form:"max_amount"`
}

// IssuingCardShippingParams is the set of parameters that can be used for the shipping parameter.
type IssuingCardShippingParams struct {
	Address *AddressParams `form:"address"`
	Name    string         `form:"name"`
	Service *string        `form:"service"`
	Type    *string        `form:"type"`

	// This parameter is deprecated. Use Service instead.
	// TODO remove in the next major version
	Speed *string `form:"speed"`
}

// IssuingCardSpendingControlsSpendingLimitParams is the set of parameters that can be used to
// represent a given spending limit for an issuing card.
type IssuingCardSpendingControlsSpendingLimitParams struct {
	Amount     *int64    `form:"amount"`
	Categories []*string `form:"categories"`
	Interval   *string   `form:"interval"`
}

// IssuingCardSpendingControlsParams is the set of parameters that can be used to configure
// the spending controls for an issuing card
type IssuingCardSpendingControlsParams struct {
	AllowedCategories      []*string                                         `form:"allowed_categories"`
	BlockedCategories      []*string                                         `form:"blocked_categories"`
	MaxApprovals           *int64                                            `form:"max_approvals"`
	SpendingLimits         []*IssuingCardSpendingControlsSpendingLimitParams `form:"spending_limits"`
	SpendingLimitsCurrency *string                                           `form:"spending_limits_currency"`
}

// IssuingCardParams is the set of parameters that can be used when creating or updating an issuing card.
type IssuingCardParams struct {
	Params            `form:"*"`
	Billing           *IssuingBillingParams              `form:"billing"`
	Cardholder        *string                            `form:"cardholder"`
	Currency          *string                            `form:"currency"`
	ReplacementFor    *string                            `form:"replacement_for"`
	ReplacementReason *string                            `form:"replacement_reason"`
	SpendingControls  *IssuingCardSpendingControlsParams `form:"spending_controls"`
	Status            *string                            `form:"status"`
	Shipping          *IssuingCardShippingParams         `form:"shipping"`
	Type              *string                            `form:"type"`

	// The following parameter is deprecated, use SpendingControls instead.
	AuthorizationControls *AuthorizationControlsParams `form:"authorization_controls"`

	// The following parameter is deprecated
	Name *string `form:"name"`
}

// IssuingCardListParams is the set of parameters that can be used when listing issuing cards.
type IssuingCardListParams struct {
	ListParams   `form:"*"`
	Cardholder   *string           `form:"cardholder"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	ExpMonth     *int64            `form:"exp_month"`
	ExpYear      *int64            `form:"exp_year"`
	Last4        *string           `form:"last4"`
	Status       *string           `form:"status"`
	Type         *string           `form:"type"`

	// The following parameters are deprecated
	Name   *string `form:"name"`
	Source *string `form:"source"`
}

// IssuingCardDetails is the resource representing issuing card details.
type IssuingCardDetails struct {
	Card     *IssuingCard `json:"card"`
	CVC      string       `json:"cvc"`
	ExpMonth *string      `form:"exp_month"`
	ExpYear  *string      `form:"exp_year"`
	Number   string       `json:"number"`
	Object   string       `json:"object"`
}

// IssuingAuthorizationControlsSpendingLimits is the resource representing spending limits
// associated with a card or cardholder.
type IssuingAuthorizationControlsSpendingLimits struct {
	Amount     int64                        `json:"amount"`
	Categories []string                     `json:"categories"`
	Interval   IssuingSpendingLimitInterval `json:"interval"`
}

// IssuingCardAuthorizationControls is the resource representing authorization controls on an issuing card.
// TODO: Add the Cardholder version to "un-share" between Card and Cardholder in the next major version.
type IssuingCardAuthorizationControls struct {
	AllowedCategories      []string                                      `json:"allowed_categories"`
	BlockedCategories      []string                                      `json:"blocked_categories"`
	MaxApprovals           int64                                         `json:"max_approvals"`
	SpendingLimits         []*IssuingAuthorizationControlsSpendingLimits `json:"spending_limits"`
	SpendingLimitsCurrency Currency                                      `json:"spending_limits_currency"`

	// The properties below are deprecated and can only be used for an issuing card.
	// TODO remove in the next major version
	Currency  Currency `json:"currency"`
	MaxAmount int64    `json:"max_amount"`
}

// IssuingCardPIN contains data about the Card's PIN.
type IssuingCardPIN struct {
	Status IssuingCardPINStatus `json:"status"`
}

// IssuingCardShipping is the resource representing shipping on an issuing card.
type IssuingCardShipping struct {
	Address        *Address                   `json:"address"`
	Carrier        string                     `json:"carrier"`
	ETA            int64                      `json:"eta"`
	Name           string                     `json:"name"`
	Phone          string                     `json:"phone"`
	Service        IssuingCardShippingService `json:"service"`
	Status         IssuingCardShippingStatus  `json:"status"`
	TrackingNumber string                     `json:"tracking_number"`
	TrackingURL    string                     `json:"tracking_url"`
	Type           IssuingCardShippingType    `json:"type"`

	// The property is deprecated. Use AddressPostalCodeCheck instead.
	// TODO remove in the next major version
	Speed IssuingCardShippingSpeed `json:"speed"`
}

// IssuingCardSpendingControlsSpendingLimit is the resource representing a spending limit
// for an issuing card.
type IssuingCardSpendingControlsSpendingLimit struct {
	Amount     int64                                            `json:"amount"`
	Categories []string                                         `json:"categories"`
	Interval   IssuingCardSpendingControlsSpendingLimitInterval `json:"interval"`
}

// IssuingCardSpendingControls is the resource representing spending controls
// for an issuing card.
type IssuingCardSpendingControls struct {
	AllowedCategories      []string                                    `json:"allowed_categories"`
	BlockedCategories      []string                                    `json:"blocked_categories"`
	MaxApprovals           int64                                       `json:"max_approvals"`
	SpendingLimits         []*IssuingCardSpendingControlsSpendingLimit `json:"spending_limits"`
	SpendingLimitsCurrency Currency                                    `json:"spending_limits_currency"`
}

// IssuingCard is the resource representing a Stripe issuing card.
type IssuingCard struct {
	Billing           *IssuingBilling              `json:"billing"`
	Brand             string                       `json:"brand"`
	Cardholder        *IssuingCardholder           `json:"cardholder"`
	Created           int64                        `json:"created"`
	ExpMonth          int64                        `json:"exp_month"`
	ExpYear           int64                        `json:"exp_year"`
	Last4             string                       `json:"last4"`
	ID                string                       `json:"id"`
	Livemode          bool                         `json:"livemode"`
	Metadata          map[string]string            `json:"metadata"`
	Object            string                       `json:"object"`
	PIN               *IssuingCardPIN              `json:"pin"`
	ReplacedBy        *IssuingCard                 `json:"replaced_by"`
	ReplacementFor    *IssuingCard                 `json:"replacement_for"`
	ReplacementReason IssuingCardReplacementReason `json:"replacement_reason"`
	Shipping          *IssuingCardShipping         `json:"shipping"`
	SpendingControls  *IssuingCardSpendingControls `json:"spending_controls"`
	Status            IssuingCardStatus            `json:"status"`
	Type              IssuingCardType              `json:"type"`

	// The following property is deprecated, use SpendingControls instead.
	AuthorizationControls *IssuingCardAuthorizationControls `json:"authorization_controls"`

	// The following property is deprecated, use Cardholder.Name instead.
	Name string `json:"name"`
}

// IssuingCardList is a list of issuing cards as retrieved from a list endpoint.
type IssuingCardList struct {
	ListMeta
	Data []*IssuingCard `json:"data"`
}

// UnmarshalJSON handles deserialization of an IssuingCard.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (i *IssuingCard) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		i.ID = id
		return nil
	}

	type issuingCard IssuingCard
	var v issuingCard
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*i = IssuingCard(v)
	return nil
}
<commit_msg>Add support for `CancellationReason` on Issuing `Card`<commit_after>package stripe

import "encoding/json"

// IssuingCardPINStatus is the list of possible values for the status field of a Card PIN.
type IssuingCardPINStatus string

// List of values that IssuingCardPINStatus can take.
const (
	IssuingCardPINStatusActive  IssuingCardPINStatus = "active"
	IssuingCardPINStatusBlocked IssuingCardPINStatus = "blocked"
)

// IssuingCardCancellationReason is the list of possible values for the cancellation reason
// on an issuing card.
type IssuingCardCancellationReason string

// List of values that IssuingCardReplacementReason can take.
const (
	IssuingCardCancellationReasonLost   IssuingCardCancellationReason = "lost"
	IssuingCardCancellationReasonStolen IssuingCardCancellationReason = "stolen"
)

// IssuingCardReplacementReason is the list of possible values for the replacement reason on an
// issuing card.
type IssuingCardReplacementReason string

// List of values that IssuingCardReplacementReason can take.
const (
	IssuingCardReplacementReasonDamaged IssuingCardReplacementReason = "damaged"
	IssuingCardReplacementReasonExpired IssuingCardReplacementReason = "expired"
	IssuingCardReplacementReasonLost    IssuingCardReplacementReason = "lost"
	IssuingCardReplacementReasonStolen  IssuingCardReplacementReason = "stolen"

	// The following values are deprecated and will be removed in the next major version.
	IssuingCardReplacementReasonDamage     IssuingCardReplacementReason = "damage"
	IssuingCardReplacementReasonExpiration IssuingCardReplacementReason = "expiration"
	IssuingCardReplacementReasonLoss       IssuingCardReplacementReason = "loss"
	IssuingCardReplacementReasonTheft      IssuingCardReplacementReason = "theft"
)

// IssuingCardShippingStatus is the list of possible values for the shipping status
// on an issuing card.
type IssuingCardShippingStatus string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingCardShippingTypeDelivered IssuingCardShippingStatus = "delivered"
	IssuingCardShippingTypeFailure   IssuingCardShippingStatus = "failure"
	IssuingCardShippingTypePending   IssuingCardShippingStatus = "pending"
	IssuingCardShippingTypeReturned  IssuingCardShippingStatus = "returned"
	IssuingCardShippingTypeShipped   IssuingCardShippingStatus = "shipped"
)

// IssuingCardShippingService is the shipment service for a card.
type IssuingCardShippingService string

// List of values that IssuingCardShippingService can take.
const (
	IssuingCardShippingServiceExpress  IssuingCardShippingService = "express"
	IssuingCardShippingServicePriority IssuingCardShippingService = "priority"
	IssuingCardShippingServiceStandard IssuingCardShippingService = "standard"

	// The following value is deprecated, use IssuingCardShippingServicePriority instead
	IssuingCardShippingServiceOvernight IssuingCardShippingService = "overnight"
)

// IssuingCardShippingSpeed is the shipment speed for a card.
// This is deprecated, use IssuingCardShippingService instead
type IssuingCardShippingSpeed string

// List of values that IssuingCardShippingSpeed can take
const (
	IssuingCardShippingSpeedExpress   IssuingCardShippingSpeed = "express"
	IssuingCardShippingSpeedOvernight IssuingCardShippingSpeed = "overnight"
	IssuingCardShippingSpeedStandard  IssuingCardShippingSpeed = "standard"
)

// IssuingCardShippingType is the list of possible values for the shipping type
// on an issuing card.
type IssuingCardShippingType string

// List of values that IssuingCardShippingType can take.
const (
	IssuingCardShippingTypeBulk       IssuingCardShippingType = "bulk"
	IssuingCardShippingTypeIndividual IssuingCardShippingType = "individual"
)

// IssuingCardSpendingControlsSpendingLimitInterval is the list of possible values for the interval
// for a spending limit on an issuing card.
type IssuingCardSpendingControlsSpendingLimitInterval string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingCardSpendingControlsSpendingLimitIntervalAllTime          IssuingCardSpendingControlsSpendingLimitInterval = "all_time"
	IssuingCardSpendingControlsSpendingLimitIntervalDaily            IssuingCardSpendingControlsSpendingLimitInterval = "daily"
	IssuingCardSpendingControlsSpendingLimitIntervalMonthly          IssuingCardSpendingControlsSpendingLimitInterval = "monthly"
	IssuingCardSpendingControlsSpendingLimitIntervalPerAuthorization IssuingCardSpendingControlsSpendingLimitInterval = "per_authorization"
	IssuingCardSpendingControlsSpendingLimitIntervalWeekly           IssuingCardSpendingControlsSpendingLimitInterval = "weekly"
	IssuingCardSpendingControlsSpendingLimitIntervalYearly           IssuingCardSpendingControlsSpendingLimitInterval = "yearly"
)

// IssuingCardStatus is the list of possible values for status on an issuing card.
type IssuingCardStatus string

// List of values that IssuingCardStatus can take.
const (
	IssuingCardStatusActive   IssuingCardStatus = "active"
	IssuingCardStatusCanceled IssuingCardStatus = "canceled"
	IssuingCardStatusInactive IssuingCardStatus = "inactive"
	IssuingCardStatusPending  IssuingCardStatus = "pending"
)

// IssuingCardType is the type of an issuing card.
type IssuingCardType string

// List of values that IssuingCardType can take.
const (
	IssuingCardTypePhysical IssuingCardType = "physical"
	IssuingCardTypeVirtual  IssuingCardType = "virtual"
)

// IssuingSpendingLimitInterval is the list of possible values for the interval of a given
// spending limit on an issuing card or cardholder.
// This is deprecated, use IssuingCardSpendingControlsSpendingLimitInterval instead
type IssuingSpendingLimitInterval string

// List of values that IssuingCardShippingStatus can take.
const (
	IssuingSpendingLimitIntervalAllTime          IssuingSpendingLimitInterval = "all_time"
	IssuingSpendingLimitIntervalDaily            IssuingSpendingLimitInterval = "daily"
	IssuingSpendingLimitIntervalMonthly          IssuingSpendingLimitInterval = "monthly"
	IssuingSpendingLimitIntervalPerAuthorization IssuingSpendingLimitInterval = "per_authorization"
	IssuingSpendingLimitIntervalWeekly           IssuingSpendingLimitInterval = "weekly"
	IssuingSpendingLimitIntervalYearly           IssuingSpendingLimitInterval = "yearly"
)

// IssuingAuthorizationControlsSpendingLimitsParams is the set of parameters that can be used for
// the spending limits associated with a given issuing card or cardholder.
// This is deprecated and will be removed in the next major version.
type IssuingAuthorizationControlsSpendingLimitsParams struct {
	Amount     *int64    `form:"amount"`
	Categories []*string `form:"categories"`
	Interval   *string   `form:"interval"`
}

// AuthorizationControlsParams is the set of parameters that can be used for the shipping parameter.
// This is deprecated and will be removed in the next major version.
type AuthorizationControlsParams struct {
	AllowedCategories []*string                                           `form:"allowed_categories"`
	BlockedCategories []*string                                           `form:"blocked_categories"`
	MaxApprovals      *int64                                              `form:"max_approvals"`
	SpendingLimits    []*IssuingAuthorizationControlsSpendingLimitsParams `form:"spending_limits"`

	// The following parameter only applies to Cardholder
	SpendingLimitsCurrency *string `form:"spending_limits_currency"`

	// The following parameter is deprecated
	MaxAmount *int64 `form:"max_amount"`
}

// IssuingCardShippingParams is the set of parameters that can be used for the shipping parameter.
type IssuingCardShippingParams struct {
	Address *AddressParams `form:"address"`
	Name    string         `form:"name"`
	Service *string        `form:"service"`
	Type    *string        `form:"type"`

	// This parameter is deprecated. Use Service instead.
	// TODO remove in the next major version
	Speed *string `form:"speed"`
}

// IssuingCardSpendingControlsSpendingLimitParams is the set of parameters that can be used to
// represent a given spending limit for an issuing card.
type IssuingCardSpendingControlsSpendingLimitParams struct {
	Amount     *int64    `form:"amount"`
	Categories []*string `form:"categories"`
	Interval   *string   `form:"interval"`
}

// IssuingCardSpendingControlsParams is the set of parameters that can be used to configure
// the spending controls for an issuing card
type IssuingCardSpendingControlsParams struct {
	AllowedCategories      []*string                                         `form:"allowed_categories"`
	BlockedCategories      []*string                                         `form:"blocked_categories"`
	MaxApprovals           *int64                                            `form:"max_approvals"`
	SpendingLimits         []*IssuingCardSpendingControlsSpendingLimitParams `form:"spending_limits"`
	SpendingLimitsCurrency *string                                           `form:"spending_limits_currency"`
}

// IssuingCardParams is the set of parameters that can be used when creating or updating an issuing card.
type IssuingCardParams struct {
	Params             `form:"*"`
	Billing            *IssuingBillingParams              `form:"billing"`
	CancellationReason *string                            `form:"cancellation_reason"`
	Cardholder         *string                            `form:"cardholder"`
	Currency           *string                            `form:"currency"`
	ReplacementFor     *string                            `form:"replacement_for"`
	ReplacementReason  *string                            `form:"replacement_reason"`
	SpendingControls   *IssuingCardSpendingControlsParams `form:"spending_controls"`
	Status             *string                            `form:"status"`
	Shipping           *IssuingCardShippingParams         `form:"shipping"`
	Type               *string                            `form:"type"`

	// The following parameter is deprecated, use SpendingControls instead.
	AuthorizationControls *AuthorizationControlsParams `form:"authorization_controls"`

	// The following parameter is deprecated
	Name *string `form:"name"`
}

// IssuingCardListParams is the set of parameters that can be used when listing issuing cards.
type IssuingCardListParams struct {
	ListParams   `form:"*"`
	Cardholder   *string           `form:"cardholder"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	ExpMonth     *int64            `form:"exp_month"`
	ExpYear      *int64            `form:"exp_year"`
	Last4        *string           `form:"last4"`
	Status       *string           `form:"status"`
	Type         *string           `form:"type"`

	// The following parameters are deprecated
	Name   *string `form:"name"`
	Source *string `form:"source"`
}

// IssuingCardDetails is the resource representing issuing card details.
type IssuingCardDetails struct {
	Card     *IssuingCard `json:"card"`
	CVC      string       `json:"cvc"`
	ExpMonth *string      `form:"exp_month"`
	ExpYear  *string      `form:"exp_year"`
	Number   string       `json:"number"`
	Object   string       `json:"object"`
}

// IssuingAuthorizationControlsSpendingLimits is the resource representing spending limits
// associated with a card or cardholder.
type IssuingAuthorizationControlsSpendingLimits struct {
	Amount     int64                        `json:"amount"`
	Categories []string                     `json:"categories"`
	Interval   IssuingSpendingLimitInterval `json:"interval"`
}

// IssuingCardAuthorizationControls is the resource representing authorization controls on an issuing card.
// TODO: Add the Cardholder version to "un-share" between Card and Cardholder in the next major version.
type IssuingCardAuthorizationControls struct {
	AllowedCategories      []string                                      `json:"allowed_categories"`
	BlockedCategories      []string                                      `json:"blocked_categories"`
	MaxApprovals           int64                                         `json:"max_approvals"`
	SpendingLimits         []*IssuingAuthorizationControlsSpendingLimits `json:"spending_limits"`
	SpendingLimitsCurrency Currency                                      `json:"spending_limits_currency"`

	// The properties below are deprecated and can only be used for an issuing card.
	// TODO remove in the next major version
	Currency  Currency `json:"currency"`
	MaxAmount int64    `json:"max_amount"`
}

// IssuingCardPIN contains data about the Card's PIN.
type IssuingCardPIN struct {
	Status IssuingCardPINStatus `json:"status"`
}

// IssuingCardShipping is the resource representing shipping on an issuing card.
type IssuingCardShipping struct {
	Address        *Address                   `json:"address"`
	Carrier        string                     `json:"carrier"`
	ETA            int64                      `json:"eta"`
	Name           string                     `json:"name"`
	Phone          string                     `json:"phone"`
	Service        IssuingCardShippingService `json:"service"`
	Status         IssuingCardShippingStatus  `json:"status"`
	TrackingNumber string                     `json:"tracking_number"`
	TrackingURL    string                     `json:"tracking_url"`
	Type           IssuingCardShippingType    `json:"type"`

	// The property is deprecated. Use AddressPostalCodeCheck instead.
	// TODO remove in the next major version
	Speed IssuingCardShippingSpeed `json:"speed"`
}

// IssuingCardSpendingControlsSpendingLimit is the resource representing a spending limit
// for an issuing card.
type IssuingCardSpendingControlsSpendingLimit struct {
	Amount     int64                                            `json:"amount"`
	Categories []string                                         `json:"categories"`
	Interval   IssuingCardSpendingControlsSpendingLimitInterval `json:"interval"`
}

// IssuingCardSpendingControls is the resource representing spending controls
// for an issuing card.
type IssuingCardSpendingControls struct {
	AllowedCategories      []string                                    `json:"allowed_categories"`
	BlockedCategories      []string                                    `json:"blocked_categories"`
	MaxApprovals           int64                                       `json:"max_approvals"`
	SpendingLimits         []*IssuingCardSpendingControlsSpendingLimit `json:"spending_limits"`
	SpendingLimitsCurrency Currency                                    `json:"spending_limits_currency"`
}

// IssuingCard is the resource representing a Stripe issuing card.
type IssuingCard struct {
	Billing            *IssuingBilling               `json:"billing"`
	Brand              string                        `json:"brand"`
	CancellationReason IssuingCardCancellationReason `json:"cancellation_reason"`
	Cardholder         *IssuingCardholder            `json:"cardholder"`
	Created            int64                         `json:"created"`
	ExpMonth           int64                         `json:"exp_month"`
	ExpYear            int64                         `json:"exp_year"`
	Last4              string                        `json:"last4"`
	ID                 string                        `json:"id"`
	Livemode           bool                          `json:"livemode"`
	Metadata           map[string]string             `json:"metadata"`
	Object             string                        `json:"object"`
	PIN                *IssuingCardPIN               `json:"pin"`
	ReplacedBy         *IssuingCard                  `json:"replaced_by"`
	ReplacementFor     *IssuingCard                  `json:"replacement_for"`
	ReplacementReason  IssuingCardReplacementReason  `json:"replacement_reason"`
	Shipping           *IssuingCardShipping          `json:"shipping"`
	SpendingControls   *IssuingCardSpendingControls  `json:"spending_controls"`
	Status             IssuingCardStatus             `json:"status"`
	Type               IssuingCardType               `json:"type"`

	// The following property is deprecated, use SpendingControls instead.
	AuthorizationControls *IssuingCardAuthorizationControls `json:"authorization_controls"`

	// The following property is deprecated, use Cardholder.Name instead.
	Name string `json:"name"`
}

// IssuingCardList is a list of issuing cards as retrieved from a list endpoint.
type IssuingCardList struct {
	ListMeta
	Data []*IssuingCard `json:"data"`
}

// UnmarshalJSON handles deserialization of an IssuingCard.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (i *IssuingCard) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		i.ID = id
		return nil
	}

	type issuingCard IssuingCard
	var v issuingCard
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*i = IssuingCard(v)
	return nil
}
<|endoftext|>


<commit_before>package storage

import (
	"errors"
	"fmt"
	"github.com/viant/afs/file"
	"github.com/viant/endly"
	"github.com/viant/toolbox/url"
	"os"
	"strings"
)

//UploadRequest represents a resources Upload request, it takes context state key to Upload to target destination.
type UploadRequest struct {
	SourceKey string        `required:"true" description:"state key with asset content"`
	Mode      int           `description:"os.FileMode"`
	Dest      *url.Resource `required:"true" description:"destination asset or directory"` //target URL with credentials
}

//UploadResponse represents a Upload response
type UploadResponse struct {
	Size int
	URL  string
}

//Upload upload content defined by sourceKey to dest
func (s *service) Upload(context *endly.Context, request *UploadRequest) (*UploadResponse, error) {
	var response = &UploadResponse{}
	return response, s.upload(context, request, response)
}

func (s *service) upload(context *endly.Context, request *UploadRequest, response *UploadResponse) error {
	dest, storageOpts, err := GetResourceWithOptions(context, request.Dest)
	if err != nil {
		return err
	}
	fs, err := StorageService(context, dest)
	if err != nil {
		return err
	}
	var state = context.State()
	if !state.Has(request.SourceKey) {
		return fmt.Errorf("sourcekey %v value was empty", request.SourceKey)

	}
	data := state.GetString(request.SourceKey)
	err = fs.Upload(context.Background(), dest.URL, os.FileMode(request.Mode), strings.NewReader(data), storageOpts...)
	if err != nil {
		return err
	}
	response.Size = len(data)
	response.URL = dest.URL
	return nil

}

//Init initialises Upload request
func (r *UploadRequest) Init() error {
	if r.Mode == 0 {
		r.Mode = int(file.DefaultFileOsMode)
	}
	return nil
}

//Validate checks if request is valid
func (r *UploadRequest) Validate() error {
	if r.Dest == nil {
		return errors.New("dest was empty")
	}
	if r.SourceKey == "" {
		return errors.New("sourceKey was empty")
	}
	return nil
}
<commit_msg>updated payload substitution<commit_after>package storage

import (
	"errors"
	"fmt"
	"github.com/viant/afs/file"
	"github.com/viant/endly"
	"github.com/viant/toolbox/url"
	"os"
	"strings"
)

//UploadRequest represents a resources Upload request, it takes context state key to Upload to target destination.
type UploadRequest struct {
	SourceKey string        `required:"true" description:"state key with asset content"`
	Mode      int           `description:"os.FileMode"`
	Dest      *url.Resource `required:"true" description:"destination asset or directory"` //target URL with credentials
}

//UploadResponse represents a Upload response
type UploadResponse struct {
	Size int
	URL  string
}

//Upload upload content defined by sourceKey to dest
func (s *service) Upload(context *endly.Context, request *UploadRequest) (*UploadResponse, error) {
	var response = &UploadResponse{}
	return response, s.upload(context, request, response)
}

func (s *service) upload(context *endly.Context, request *UploadRequest, response *UploadResponse) error {
	dest, storageOpts, err := GetResourceWithOptions(context, request.Dest)
	if err != nil {
		return err
	}
	fs, err := StorageService(context, dest)
	if err != nil {
		return err
	}
	var state = context.State()
	if !state.Has(request.SourceKey) {
		return fmt.Errorf("sourcekey %v value was empty", request.SourceKey)
	}
	data := state.GetString(request.SourceKey)
	data = context.Expand(data)
	err = fs.Upload(context.Background(), dest.URL, os.FileMode(request.Mode), strings.NewReader(data), storageOpts...)
	if err != nil {
		return err
	}
	response.Size = len(data)
	response.URL = dest.URL
	return nil

}

//Init initialises Upload request
func (r *UploadRequest) Init() error {
	if r.Mode == 0 {
		r.Mode = int(file.DefaultFileOsMode)
	}
	return nil
}

//Validate checks if request is valid
func (r *UploadRequest) Validate() error {
	if r.Dest == nil {
		return errors.New("dest was empty")
	}
	if r.SourceKey == "" {
		return errors.New("sourceKey was empty")
	}
	return nil
}
<|endoftext|>


<commit_before>package shape

import (
	"github.com/oakmound/oak/alg/intgeom"
)

// A Rect is a function that returns a 2d boolean array
// of booleans for a given size, where true represents
// that the bounded shape contains the point [x][y].
type Rect func(sizes ...int) [][]bool

// InToRect converts an In function into a Rect function.
// Know that, if you are planning on looping over this only
// once, it's better to just use the In function. The use
// case for this is if the same size rect will be queried
// on some function multiple times, and just having the booleans
// to re-access is needed.
func InToRect(i In) Rect {
	return func(sizes ...int) [][]bool {
		w := sizes[0]
		h := sizes[0]
		if len(sizes) > 1 {
			h = sizes[1]
		}
		out := make([][]bool, w)
		for x := range out {
			out[x] = make([]bool, h)
			for y := range out[x] {
				out[x][y] = i(x, y, sizes...)
			}
		}
		return out
	}
}

// A StrictRect is a shape that ignores input width and height given to it.
type StrictRect [][]bool

// NewStrictRect returns a StrictRect with the given strict dimensions, all
// values set fo false.
func NewStrictRect(w, h int) StrictRect {
	sh := make(StrictRect, w)
	for x := range sh {
		sh[x] = make([]bool, h)
	}
	return sh
}

// In returns whether the input x and y are within this StrictRect's shape.
// If the shape is undefined for the input values, it returns false.
func (sr StrictRect) In(x, y int, sizes ...int) bool {
	if x >= len(sr) {
		return false
	}
	if y >= len(sr[x]) {
		return false
	}
	return sr[x][y]
}

// Outline returns this StrictRect's outline, ignoring the input dimensions.
func (sr StrictRect) Outline(sizes ...int) ([]intgeom.Point2, error) {
	return ToOutline(sr)(len(sr), len(sr[0]))
}

// Rect returns the StrictRect itself.
func (sr StrictRect) Rect(sizes ...int) [][]bool {
	return sr
}
<commit_msg>Update rect.go<commit_after>package shape

import (
	"github.com/oakmound/oak/alg/intgeom"
)

// A Rect is a function that returns a 2d boolean array
// of booleans for a given size, where true represents
// that the bounded shape contains the point [x][y].
type Rect func(sizes ...int) [][]bool

// InToRect converts an In function into a Rect function.
// Know that, if you are planning on looping over this only
// once, it's better to just use the In function. The use
// case for this is if the same size rect will be queried
// on some function multiple times, and just having the booleans
// to re-access is needed.
func InToRect(i In) Rect {
	return func(sizes ...int) [][]bool {
		w := sizes[0]
		h := sizes[0]
		if len(sizes) > 1 {
			h = sizes[1]
		}
		out := make([][]bool, w)
		for x := range out {
			out[x] = make([]bool, h)
			for y := range out[x] {
				out[x][y] = i(x, y, sizes...)
			}
		}
		return out
	}
}

// A StrictRect is a shape that ignores input width and height given to it.
type StrictRect [][]bool

// NewStrictRect returns a StrictRect with the given strict dimensions, all
// values set to false.
func NewStrictRect(w, h int) StrictRect {
	sh := make(StrictRect, w)
	for x := range sh {
		sh[x] = make([]bool, h)
	}
	return sh
}

// In returns whether the input x and y are within this StrictRect's shape.
// If the shape is undefined for the input values, it returns false.
func (sr StrictRect) In(x, y int, sizes ...int) bool {
	if x >= len(sr) {
		return false
	}
	if y >= len(sr[x]) {
		return false
	}
	return sr[x][y]
}

// Outline returns this StrictRect's outline, ignoring the input dimensions.
func (sr StrictRect) Outline(sizes ...int) ([]intgeom.Point2, error) {
	return ToOutline(sr)(len(sr), len(sr[0]))
}

// Rect returns the StrictRect itself.
func (sr StrictRect) Rect(sizes ...int) [][]bool {
	return sr
}
<|endoftext|>


<commit_before>// Copyright Istio 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 helm

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/mholt/archiver/v3"

	"istio.io/istio/operator/pkg/httprequest"
	"istio.io/istio/operator/pkg/version"
)

const (
	// InstallationDirectory is temporary folder name for caching downloaded installation packages.
	InstallationDirectory = "istio-install-packages"
	// OperatorSubdirFilePath is file path of installation packages to helm charts.
	OperatorSubdirFilePath = "manifests"
	// OperatorSubdirFilePath15 is the file path of installation packages to helm charts for 1.5 and earlier.
	// TODO: remove in 1.7.
	OperatorSubdirFilePath15 = "install/kubernetes/operator"
)

// URLFetcher is used to fetch and manipulate charts from remote url
type URLFetcher struct {
	// url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz
	// i.e. the full URL path to the Istio release tar.
	url string
	// destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be
	// set to the default value, which is static for caching purposes.
	destDirRoot string
}

// NewURLFetcher creates an URLFetcher pointing to installation package URL and destination dir to extract it into,
// and returns a pointer to it.
// url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz
// i.e. the full URL path to the Istio release tar.
// destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be set
// to the default value, which is static for caching purposes.
func NewURLFetcher(url string, destDirRoot string) *URLFetcher {
	if destDirRoot == "" {
		destDirRoot = filepath.Join(os.TempDir(), InstallationDirectory)
	}
	return &URLFetcher{
		url:         url,
		destDirRoot: destDirRoot,
	}
}

// DestDir returns path of destination dir that the tar was extracted to.
func (f *URLFetcher) DestDir() string {
	// checked for error during download.
	subdir, _, _ := URLToDirname(f.url)
	return filepath.Join(f.destDirRoot, subdir)
}

// Fetch fetches and untars the charts.
func (f *URLFetcher) Fetch() error {
	if _, _, err := URLToDirname(f.url); err != nil {
		return err
	}
	saved, err := DownloadTo(f.url, f.destDirRoot)
	if err != nil {
		return err
	}
	file, err := os.Open(saved)
	if err != nil {
		return err
	}
	defer file.Close()

	targz := archiver.TarGz{Tar: &archiver.Tar{OverwriteExisting: true}}
	return targz.Unarchive(saved, f.destDirRoot)
}

// DownloadTo downloads from remote srcURL to dest local file path
func DownloadTo(srcURL, dest string) (string, error) {
	u, err := url.Parse(srcURL)
	if err != nil {
		return "", fmt.Errorf("invalid chart URL: %s", srcURL)
	}
	data, err := httprequest.Get(u.String())
	if err != nil {
		return "", err
	}

	name := filepath.Base(u.Path)
	destFile := filepath.Join(dest, name)
	dir := filepath.Dir(destFile)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.Mkdir(dir, 0755)
		if err != nil {
			return "", err
		}
	}

	if err := ioutil.WriteFile(destFile, data, 0644); err != nil {
		return destFile, err
	}

	return destFile, nil
}

// URLToDirname, given an input URL pointing to an Istio release tar, returns the subdirectory name that the tar would
// be extracted to and the version in the URL. The input URLs are expected to have the form
// https://.../istio-{version}-{platform}[optional suffix].tar.gz.
func URLToDirname(url string) (string, *version.Version, error) {
	fn := path.Base(url)
	fv := strings.Split(fn, "-")
	fvl := len(fv)
	if fvl < 2 || fv[0] != "istio" {
		return "", nil, fmt.Errorf("wrong format for release tar name, got: %s, expect https://.../istio-{version}-{platform}.tar.gz", url)
	}
	ver, err := version.NewVersionFromString(fv[1])
	if err != nil {
		return "", nil, err
	}
	// get rid of the suffix like -linux-arch or -osx
	if fv[fvl-2] == "linux" {
		// linux names also have arch appended, trim this off
		fv = fv[:fvl-1]
	}

	return strings.Join(fv[:len(fv)-1], "-"), ver, nil
}
<commit_msg>Remove redundant open files (#32707)<commit_after>// Copyright Istio 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 helm

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/mholt/archiver/v3"

	"istio.io/istio/operator/pkg/httprequest"
	"istio.io/istio/operator/pkg/version"
)

const (
	// InstallationDirectory is temporary folder name for caching downloaded installation packages.
	InstallationDirectory = "istio-install-packages"
	// OperatorSubdirFilePath is file path of installation packages to helm charts.
	OperatorSubdirFilePath = "manifests"
	// OperatorSubdirFilePath15 is the file path of installation packages to helm charts for 1.5 and earlier.
	// TODO: remove in 1.7.
	OperatorSubdirFilePath15 = "install/kubernetes/operator"
)

// URLFetcher is used to fetch and manipulate charts from remote url
type URLFetcher struct {
	// url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz
	// i.e. the full URL path to the Istio release tar.
	url string
	// destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be
	// set to the default value, which is static for caching purposes.
	destDirRoot string
}

// NewURLFetcher creates an URLFetcher pointing to installation package URL and destination dir to extract it into,
// and returns a pointer to it.
// url is the source URL where release tar is downloaded from. It should be in the form https://.../istio-{version}-{platform}.tar.gz
// i.e. the full URL path to the Istio release tar.
// destDirRoot is the root dir where charts are downloaded and extracted. If set to "", the destination dir will be set
// to the default value, which is static for caching purposes.
func NewURLFetcher(url string, destDirRoot string) *URLFetcher {
	if destDirRoot == "" {
		destDirRoot = filepath.Join(os.TempDir(), InstallationDirectory)
	}
	return &URLFetcher{
		url:         url,
		destDirRoot: destDirRoot,
	}
}

// DestDir returns path of destination dir that the tar was extracted to.
func (f *URLFetcher) DestDir() string {
	// checked for error during download.
	subdir, _, _ := URLToDirname(f.url)
	return filepath.Join(f.destDirRoot, subdir)
}

// Fetch fetches and untars the charts.
func (f *URLFetcher) Fetch() error {
	if _, _, err := URLToDirname(f.url); err != nil {
		return err
	}
	saved, err := DownloadTo(f.url, f.destDirRoot)
	if err != nil {
		return err
	}

	targz := archiver.TarGz{Tar: &archiver.Tar{OverwriteExisting: true}}
	return targz.Unarchive(saved, f.destDirRoot)
}

// DownloadTo downloads from remote srcURL to dest local file path
func DownloadTo(srcURL, dest string) (string, error) {
	u, err := url.Parse(srcURL)
	if err != nil {
		return "", fmt.Errorf("invalid chart URL: %s", srcURL)
	}
	data, err := httprequest.Get(u.String())
	if err != nil {
		return "", err
	}

	name := filepath.Base(u.Path)
	destFile := filepath.Join(dest, name)
	dir := filepath.Dir(destFile)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.Mkdir(dir, 0755)
		if err != nil {
			return "", err
		}
	}

	if err := ioutil.WriteFile(destFile, data, 0644); err != nil {
		return destFile, err
	}

	return destFile, nil
}

// URLToDirname, given an input URL pointing to an Istio release tar, returns the subdirectory name that the tar would
// be extracted to and the version in the URL. The input URLs are expected to have the form
// https://.../istio-{version}-{platform}[optional suffix].tar.gz.
func URLToDirname(url string) (string, *version.Version, error) {
	fn := path.Base(url)
	fv := strings.Split(fn, "-")
	fvl := len(fv)
	if fvl < 2 || fv[0] != "istio" {
		return "", nil, fmt.Errorf("wrong format for release tar name, got: %s, expect https://.../istio-{version}-{platform}.tar.gz", url)
	}
	ver, err := version.NewVersionFromString(fv[1])
	if err != nil {
		return "", nil, err
	}
	// get rid of the suffix like -linux-arch or -osx
	if fv[fvl-2] == "linux" {
		// linux names also have arch appended, trim this off
		fv = fv[:fvl-1]
	}

	return strings.Join(fv[:len(fv)-1], "-"), ver, nil
}
<|endoftext|>


<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 fastcgi

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
	"github.com/caddyserver/caddy/v2/modules/caddytls"

	"github.com/caddyserver/caddy/v2"
)

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

// Transport facilitates FastCGI communication.
type Transport struct {
	// Use this directory as the fastcgi root directory. Defaults to the root
	// directory of the parent virtual host.
	Root string `json:"root,omitempty"`

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	//
	// Future enhancements should be careful to avoid CVE-2019-11043,
	// which can be mitigated with use of a try_files-like behavior
	// that 404s if the fastcgi path info is not found.
	SplitPath []string `json:"split_path,omitempty"`

	// Extra environment variables.
	EnvVars map[string]string `json:"env,omitempty"`

	// The duration used to set a deadline when connecting to an upstream.
	DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`

	// The duration used to set a deadline when reading from the FastCGI server.
	ReadTimeout caddy.Duration `json:"read_timeout,omitempty"`

	// The duration used to set a deadline when sending to the FastCGI server.
	WriteTimeout caddy.Duration `json:"write_timeout,omitempty"`

	serverSoftware string
}

// CaddyModule returns the Caddy module information.
func (Transport) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.reverse_proxy.transport.fastcgi",
		New: func() caddy.Module { return new(Transport) },
	}
}

// Provision sets up t.
func (t *Transport) Provision(_ caddy.Context) error {
	if t.Root == "" {
		t.Root = "{http.vars.root}"
	}
	t.serverSoftware = "Caddy"
	if mod := caddy.GoModule(); mod.Version != "" {
		t.serverSoftware += "/" + mod.Version
	}
	return nil
}

// RoundTrip implements http.RoundTripper.
func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
	env, err := t.buildEnv(r)
	if err != nil {
		return nil, fmt.Errorf("building environment: %v", err)
	}

	// TODO: doesn't dialer have a Timeout field?
	ctx := r.Context()
	if t.DialTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, time.Duration(t.DialTimeout))
		defer cancel()
	}

	// extract dial information from request (should have been embedded by the reverse proxy)
	network, address := "tcp", r.URL.Host
	if dialInfo, ok := reverseproxy.GetDialInfo(ctx); ok {
		network = dialInfo.Network
		address = dialInfo.Address
	}

	fcgiBackend, err := DialContext(ctx, network, address)
	if err != nil {
		// TODO: wrap in a special error type if the dial failed, so retries can happen if enabled
		return nil, fmt.Errorf("dialing backend: %v", err)
	}
	// fcgiBackend gets closed when response body is closed (see clientCloser)

	// read/write timeouts
	if err := fcgiBackend.SetReadTimeout(time.Duration(t.ReadTimeout)); err != nil {
		return nil, fmt.Errorf("setting read timeout: %v", err)
	}
	if err := fcgiBackend.SetWriteTimeout(time.Duration(t.WriteTimeout)); err != nil {
		return nil, fmt.Errorf("setting write timeout: %v", err)
	}

	contentLength := r.ContentLength
	if contentLength == 0 {
		contentLength, _ = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
	}

	var resp *http.Response
	switch r.Method {
	case http.MethodHead:
		resp, err = fcgiBackend.Head(env)
	case http.MethodGet:
		resp, err = fcgiBackend.Get(env, r.Body, contentLength)
	case http.MethodOptions:
		resp, err = fcgiBackend.Options(env)
	default:
		resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength)
	}

	return resp, err
}

// buildEnv returns a set of CGI environment variables for the request.
func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)

	var env map[string]string

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Remove [] from IPv6 addresses
	ip = strings.Replace(ip, "[", "", 1)
	ip = strings.Replace(ip, "]", "", 1)

	root := repl.ReplaceAll(t.Root, ".")
	fpath := r.URL.Path

	// Split path in preparation for env variables.
	// Previous canSplit checks ensure this can never be -1.
	// TODO: I haven't brought over canSplit from v1; make sure this doesn't break
	splitPos := t.splitPos(fpath)

	// Request has the extension; path was split successfully
	docURI := fpath[:splitPos+len(t.SplitPath)]
	pathInfo := fpath[splitPos+len(t.SplitPath):]
	scriptName := fpath

	// Strip PATH_INFO from SCRIPT_NAME
	scriptName = strings.TrimSuffix(scriptName, pathInfo)

	// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
	scriptFilename := filepath.Join(root, scriptName)

	// Add vhost path prefix to scriptName. Otherwise, some PHP software will
	// have difficulty discovering its URL.
	pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string)
	scriptName = path.Join(pathPrefix, scriptName)

	// Get the request URL from context. The context stores the original URL in case
	// it was changed by a middleware such as rewrite. By default, we pass the
	// original URI in as the value of REQUEST_URI (the user can overwrite this
	// if desired). Most PHP apps seem to want the original URI. Besides, this is
	// how nginx defaults: http://stackoverflow.com/a/12485156/1048862
	origReq, ok := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
	if !ok {
		// some requests, like active health checks, don't add this to
		// the request context, so we can just use the current URL
		origReq = *r
	}
	reqURL := origReq.URL

	requestScheme := "http"
	if r.TLS != nil {
		requestScheme = "https"
	}

	reqHost, reqPort, err := net.SplitHostPort(r.Host)
	if err != nil {
		// whatever, just assume there was no port
		reqHost = r.Host
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{
		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // TODO: once there are authentication handlers, populate this
		"REQUEST_METHOD":    r.Method,
		"REQUEST_SCHEME":    requestScheme,
		"SERVER_NAME":       reqHost,
		"SERVER_PORT":       reqPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   t.serverSoftware,

		// Other variables
		"DOCUMENT_ROOT":   root,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     reqURL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// compliance with the CGI specification requires that
	// PATH_TRANSLATED should only exist if PATH_INFO is defined.
	// Info: https://www.ietf.org/rfc/rfc3875 Page 14
	if env["PATH_INFO"] != "" {
		env["PATH_TRANSLATED"] = filepath.Join(root, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
	}

	// Some web apps rely on knowing HTTPS or not
	if r.TLS != nil {
		env["HTTPS"] = "on"
		// and pass the protocol details in a manner compatible with apache's mod_ssl
		// (which is why these have a SSL_ prefix and not TLS_).
		v, ok := tlsProtocolStrings[r.TLS.Version]
		if ok {
			env["SSL_PROTOCOL"] = v
		}
		// and pass the cipher suite in a manner compatible with apache's mod_ssl
		for k, v := range caddytls.SupportedCipherSuites {
			if v == r.TLS.CipherSuite {
				env["SSL_CIPHER"] = k
				break
			}
		}
	}

	// Add env variables from config (with support for placeholders in values)
	for key, value := range t.EnvVars {
		env[key] = repl.ReplaceAll(value, "")
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}
	return env, nil
}

// splitPos returns the index where path should
// be split based on t.SplitPath.
func (t Transport) splitPos(path string) int {
	// TODO: from v1...
	// if httpserver.CaseSensitivePath {
	// 	return strings.Index(path, r.SplitPath)
	// }
	lowerPath := strings.ToLower(path)
	for _, split := range t.SplitPath {
		if idx := strings.Index(lowerPath, strings.ToLower(split)); idx > -1 {
			return idx
		}
	}
	return -1
}

// Map of supported protocols to Apache ssl_mod format
// Note that these are slightly different from SupportedProtocols in caddytls/config.go
var tlsProtocolStrings = map[uint16]string{
	tls.VersionTLS10: "TLSv1",
	tls.VersionTLS11: "TLSv1.1",
	tls.VersionTLS12: "TLSv1.2",
	tls.VersionTLS13: "TLSv1.3",
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")

// Interface guards
var (
	_ caddy.Provisioner = (*Transport)(nil)
	_ http.RoundTripper = (*Transport)(nil)
)
<commit_msg>fastcgi: Fix PATH_INFO (issue #3178)<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 fastcgi

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
	"github.com/caddyserver/caddy/v2/modules/caddytls"

	"github.com/caddyserver/caddy/v2"
)

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

// Transport facilitates FastCGI communication.
type Transport struct {
	// Use this directory as the fastcgi root directory. Defaults to the root
	// directory of the parent virtual host.
	Root string `json:"root,omitempty"`

	// The path in the URL will be split into two, with the first piece ending
	// with the value of SplitPath. The first piece will be assumed as the
	// actual resource (CGI script) name, and the second piece will be set to
	// PATH_INFO for the CGI script to use.
	//
	// Future enhancements should be careful to avoid CVE-2019-11043,
	// which can be mitigated with use of a try_files-like behavior
	// that 404s if the fastcgi path info is not found.
	SplitPath []string `json:"split_path,omitempty"`

	// Extra environment variables.
	EnvVars map[string]string `json:"env,omitempty"`

	// The duration used to set a deadline when connecting to an upstream.
	DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`

	// The duration used to set a deadline when reading from the FastCGI server.
	ReadTimeout caddy.Duration `json:"read_timeout,omitempty"`

	// The duration used to set a deadline when sending to the FastCGI server.
	WriteTimeout caddy.Duration `json:"write_timeout,omitempty"`

	serverSoftware string
}

// CaddyModule returns the Caddy module information.
func (Transport) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:  "http.reverse_proxy.transport.fastcgi",
		New: func() caddy.Module { return new(Transport) },
	}
}

// Provision sets up t.
func (t *Transport) Provision(_ caddy.Context) error {
	if t.Root == "" {
		t.Root = "{http.vars.root}"
	}
	t.serverSoftware = "Caddy"
	if mod := caddy.GoModule(); mod.Version != "" {
		t.serverSoftware += "/" + mod.Version
	}
	return nil
}

// RoundTrip implements http.RoundTripper.
func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
	env, err := t.buildEnv(r)
	if err != nil {
		return nil, fmt.Errorf("building environment: %v", err)
	}

	// TODO: doesn't dialer have a Timeout field?
	ctx := r.Context()
	if t.DialTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, time.Duration(t.DialTimeout))
		defer cancel()
	}

	// extract dial information from request (should have been embedded by the reverse proxy)
	network, address := "tcp", r.URL.Host
	if dialInfo, ok := reverseproxy.GetDialInfo(ctx); ok {
		network = dialInfo.Network
		address = dialInfo.Address
	}

	fcgiBackend, err := DialContext(ctx, network, address)
	if err != nil {
		// TODO: wrap in a special error type if the dial failed, so retries can happen if enabled
		return nil, fmt.Errorf("dialing backend: %v", err)
	}
	// fcgiBackend gets closed when response body is closed (see clientCloser)

	// read/write timeouts
	if err := fcgiBackend.SetReadTimeout(time.Duration(t.ReadTimeout)); err != nil {
		return nil, fmt.Errorf("setting read timeout: %v", err)
	}
	if err := fcgiBackend.SetWriteTimeout(time.Duration(t.WriteTimeout)); err != nil {
		return nil, fmt.Errorf("setting write timeout: %v", err)
	}

	contentLength := r.ContentLength
	if contentLength == 0 {
		contentLength, _ = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
	}

	var resp *http.Response
	switch r.Method {
	case http.MethodHead:
		resp, err = fcgiBackend.Head(env)
	case http.MethodGet:
		resp, err = fcgiBackend.Get(env, r.Body, contentLength)
	case http.MethodOptions:
		resp, err = fcgiBackend.Options(env)
	default:
		resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength)
	}

	return resp, err
}

// buildEnv returns a set of CGI environment variables for the request.
func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)

	var env map[string]string

	// Separate remote IP and port; more lenient than net.SplitHostPort
	var ip, port string
	if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 {
		ip = r.RemoteAddr[:idx]
		port = r.RemoteAddr[idx+1:]
	} else {
		ip = r.RemoteAddr
	}

	// Remove [] from IPv6 addresses
	ip = strings.Replace(ip, "[", "", 1)
	ip = strings.Replace(ip, "]", "", 1)

	root := repl.ReplaceAll(t.Root, ".")
	fpath := r.URL.Path

	// Split path in preparation for env variables.
	// Previous canSplit checks ensure this can never be -1.
	// TODO: I haven't brought over canSplit from v1; make sure this doesn't break
	splitPos := t.splitPos(fpath)

	// Request has the extension; path was split successfully
	docURI := fpath[:splitPos]
	pathInfo := fpath[splitPos:]
	scriptName := fpath

	// Strip PATH_INFO from SCRIPT_NAME
	scriptName = strings.TrimSuffix(scriptName, pathInfo)

	// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
	scriptFilename := filepath.Join(root, scriptName)

	// Add vhost path prefix to scriptName. Otherwise, some PHP software will
	// have difficulty discovering its URL.
	pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string)
	scriptName = path.Join(pathPrefix, scriptName)

	// Get the request URL from context. The context stores the original URL in case
	// it was changed by a middleware such as rewrite. By default, we pass the
	// original URI in as the value of REQUEST_URI (the user can overwrite this
	// if desired). Most PHP apps seem to want the original URI. Besides, this is
	// how nginx defaults: http://stackoverflow.com/a/12485156/1048862
	origReq, ok := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
	if !ok {
		// some requests, like active health checks, don't add this to
		// the request context, so we can just use the current URL
		origReq = *r
	}
	reqURL := origReq.URL

	requestScheme := "http"
	if r.TLS != nil {
		requestScheme = "https"
	}

	reqHost, reqPort, err := net.SplitHostPort(r.Host)
	if err != nil {
		// whatever, just assume there was no port
		reqHost = r.Host
	}

	// Some variables are unused but cleared explicitly to prevent
	// the parent environment from interfering.
	env = map[string]string{
		// Variables defined in CGI 1.1 spec
		"AUTH_TYPE":         "", // Not used
		"CONTENT_LENGTH":    r.Header.Get("Content-Length"),
		"CONTENT_TYPE":      r.Header.Get("Content-Type"),
		"GATEWAY_INTERFACE": "CGI/1.1",
		"PATH_INFO":         pathInfo,
		"QUERY_STRING":      r.URL.RawQuery,
		"REMOTE_ADDR":       ip,
		"REMOTE_HOST":       ip, // For speed, remote host lookups disabled
		"REMOTE_PORT":       port,
		"REMOTE_IDENT":      "", // Not used
		"REMOTE_USER":       "", // TODO: once there are authentication handlers, populate this
		"REQUEST_METHOD":    r.Method,
		"REQUEST_SCHEME":    requestScheme,
		"SERVER_NAME":       reqHost,
		"SERVER_PORT":       reqPort,
		"SERVER_PROTOCOL":   r.Proto,
		"SERVER_SOFTWARE":   t.serverSoftware,

		// Other variables
		"DOCUMENT_ROOT":   root,
		"DOCUMENT_URI":    docURI,
		"HTTP_HOST":       r.Host, // added here, since not always part of headers
		"REQUEST_URI":     reqURL.RequestURI(),
		"SCRIPT_FILENAME": scriptFilename,
		"SCRIPT_NAME":     scriptName,
	}

	// compliance with the CGI specification requires that
	// PATH_TRANSLATED should only exist if PATH_INFO is defined.
	// Info: https://www.ietf.org/rfc/rfc3875 Page 14
	if env["PATH_INFO"] != "" {
		env["PATH_TRANSLATED"] = filepath.Join(root, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html
	}

	// Some web apps rely on knowing HTTPS or not
	if r.TLS != nil {
		env["HTTPS"] = "on"
		// and pass the protocol details in a manner compatible with apache's mod_ssl
		// (which is why these have a SSL_ prefix and not TLS_).
		v, ok := tlsProtocolStrings[r.TLS.Version]
		if ok {
			env["SSL_PROTOCOL"] = v
		}
		// and pass the cipher suite in a manner compatible with apache's mod_ssl
		for k, v := range caddytls.SupportedCipherSuites {
			if v == r.TLS.CipherSuite {
				env["SSL_CIPHER"] = k
				break
			}
		}
	}

	// Add env variables from config (with support for placeholders in values)
	for key, value := range t.EnvVars {
		env[key] = repl.ReplaceAll(value, "")
	}

	// Add all HTTP headers to env variables
	for field, val := range r.Header {
		header := strings.ToUpper(field)
		header = headerNameReplacer.Replace(header)
		env["HTTP_"+header] = strings.Join(val, ", ")
	}
	return env, nil
}

// splitPos returns the index where path should
// be split based on t.SplitPath.
func (t Transport) splitPos(path string) int {
	// TODO: from v1...
	// if httpserver.CaseSensitivePath {
	// 	return strings.Index(path, r.SplitPath)
	// }
	lowerPath := strings.ToLower(path)
	for _, split := range t.SplitPath {
		if idx := strings.Index(lowerPath, strings.ToLower(split)); idx > -1 {
			return idx + len(split)
		}
	}
	return -1
}

// Map of supported protocols to Apache ssl_mod format
// Note that these are slightly different from SupportedProtocols in caddytls/config.go
var tlsProtocolStrings = map[uint16]string{
	tls.VersionTLS10: "TLSv1",
	tls.VersionTLS11: "TLSv1.1",
	tls.VersionTLS12: "TLSv1.2",
	tls.VersionTLS13: "TLSv1.3",
}

var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")

// Interface guards
var (
	_ caddy.Provisioner = (*Transport)(nil)
	_ http.RoundTripper = (*Transport)(nil)
)
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
)

//Campaign is a struct representing a created campaign
type Campaign struct {
	Id            int64     `json:"id"`
	UserId        int64     `json:"-"`
	Name          string    `json:"name" sql:"not null"`
	CreatedDate   time.Time `json:"created_date"`
	CompletedDate time.Time `json:"completed_date"`
	TemplateId    int64     `json:"-"`
	Template      Template  `json:"template"`
	PageId        int64     `json:"-"`
	Page          Page      `json:"page"`
	Status        string    `json:"status"`
	Results       []Result  `json:"results,omitempty"`
	Groups        []Group   `json:"groups,omitempty"`
	Events        []Event   `json:"timeline,omitemtpy"`
	SMTPId        int64     `json:"-"`
	SMTP          SMTP      `json:"smtp"`
	URL           string    `json:"url"`
}

// ErrCampaignNameNotSpecified indicates there was no template given by the user
var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified")

// ErrGroupNotSpecified indicates there was no template given by the user
var ErrGroupNotSpecified = errors.New("No groups specified")

// ErrTemplateNotSpecified indicates there was no template given by the user
var ErrTemplateNotSpecified = errors.New("No email template specified")

// ErrPageNotSpecified indicates a landing page was not provided for the campaign
var ErrPageNotSpecified = errors.New("No landing page specified")

// ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign
var ErrSMTPNotSpecified = errors.New("No sending profile specified")

// ErrTemplateNotFound indicates the template specified does not exist in the database
var ErrTemplateNotFound = errors.New("Template not found")

// ErrGroupnNotFound indicates a group specified by the user does not exist in the database
var ErrGroupNotFound = errors.New("Group not found")

// ErrPageNotFound indicates a page specified by the user does not exist in the database
var ErrPageNotFound = errors.New("Page not found")

// ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database
var ErrSMTPNotFound = errors.New("Sending profile not found")

// Validate checks to make sure there are no invalid fields in a submitted campaign
func (c *Campaign) Validate() error {
	switch {
	case c.Name == "":
		return ErrCampaignNameNotSpecified
	case len(c.Groups) == 0:
		return ErrGroupNotSpecified
	case c.Template.Name == "":
		return ErrTemplateNotSpecified
	case c.Page.Name == "":
		return ErrPageNotSpecified
	case c.SMTP.Name == "":
		return ErrSMTPNotSpecified
	}
	return nil
}

// SendTestEmailRequest is the structure of a request
// to send a test email to test an SMTP connection
type SendTestEmailRequest struct {
	Template    Template `json:"template"`
	Page        Page     `json:"page"`
	SMTP        SMTP     `json:"smtp"`
	URL         string   `json:"url"`
	Tracker     string   `json:"tracker"`
	TrackingURL string   `json:"tracking_url"`
	From        string   `json:"from"`
	Target
}

// Validate ensures the SendTestEmailRequest structure
// is valid.
func (s *SendTestEmailRequest) Validate() error {
	switch {
	case s.Email == "":
		return ErrEmailNotSpecified
	}
	return nil
}

// UpdateStatus changes the campaign status appropriately
func (c *Campaign) UpdateStatus(s string) error {
	// This could be made simpler, but I think there's a bug in gorm
	return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
}

// AddEvent creates a new campaign event in the database
func (c *Campaign) AddEvent(e Event) error {
	e.CampaignId = c.Id
	e.Time = time.Now()
	return db.Debug().Save(&e).Error
}

// Event contains the fields for an event
// that occurs during the campaign
type Event struct {
	Id         int64     `json:"-"`
	CampaignId int64     `json:"-"`
	Email      string    `json:"email"`
	Time       time.Time `json:"time"`
	Message    string    `json:"message"`
	Details    string    `json:"details"`
}

// GetCampaigns returns the campaigns owned by the given user.
func GetCampaigns(uid int64) ([]Campaign, error) {
	cs := []Campaign{}
	err := db.Model(&User{Id: uid}).Related(&cs).Error
	if err != nil {
		fmt.Println(err)
	}
	for i, _ := range cs {
		err := db.Model(&cs[i]).Related(&cs[i].Results).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Model(&cs[i]).Related(&cs[i].Events).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("templates").Where("id=?", cs[i].TemplateId).Find(&cs[i].Template).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("pages").Where("id=?", cs[i].PageId).Find(&cs[i].Page).Error
		if err != nil {
			Logger.Println(err)
		}
		err = db.Table("SMTP").Where("id=?", cs[i].SMTPId).Find(&cs[i].SMTP).Error
		if err != nil {
			Logger.Println(err)
		}
	}
	return cs, err
}

// GetCampaign returns the campaign, if it exists, specified by the given id and user_id.
func GetCampaign(id int64, uid int64) (Campaign, error) {
	c := Campaign{}
	err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
	if err != nil {
		Logger.Printf("%s: campaign not found\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Results).Error
	if err != nil {
		Logger.Printf("%s: results not found for campaign\n", err)
		return c, err
	}
	err = db.Model(&c).Related(&c.Events).Error
	if err != nil {
		Logger.Printf("%s: events not found for campaign\n", err)
		return c, err
	}
	err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
	if err != nil {
		Logger.Printf("%s: template not found for campaign\n", err)
		return c, err
	}
	err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
	if err != nil {
		Logger.Printf("%s: page not found for campaign\n", err)
	}
	err = db.Table("SMTP").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
	if err != nil {
		// For now, since we just introduced the new sending profiles
		// we'll return nil for the error
		Logger.Printf("%s: sending profile not found for campaign\n", err)
		err = nil
	}
	return c, err
}

// PostCampaign inserts a campaign and all associated records into the database.
func PostCampaign(c *Campaign, uid int64) error {
	if err := c.Validate(); err != nil {
		return err
	}
	// Fill in the details
	c.UserId = uid
	c.CreatedDate = time.Now()
	c.CompletedDate = time.Time{}
	c.Status = CAMPAIGN_QUEUED
	// Check to make sure all the groups already exist
	for i, g := range c.Groups {
		c.Groups[i], err = GetGroupByName(g.Name, uid)
		if err == gorm.ErrRecordNotFound {
			Logger.Printf("Error - Group %s does not exist", g.Name)
			return ErrGroupNotFound
		} else if err != nil {
			Logger.Println(err)
			return err
		}
	}
	// Check to make sure the template exists
	t, err := GetTemplateByName(c.Template.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Template %s does not exist", t.Name)
		return ErrTemplateNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Template = t
	c.TemplateId = t.Id
	// Check to make sure the page exists
	p, err := GetPageByName(c.Page.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Page %s does not exist", p.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Page = p
	c.PageId = p.Id
	// Check to make sure the sending profile exists
	s, err := GetSMTPByName(c.SMTP.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Sending profile %s does not exist", s.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.SMTP = s
	c.SMTPId = s.Id
	// Insert into the DB
	err = db.Save(c).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = c.AddEvent(Event{Message: "Campaign Created"})
	if err != nil {
		Logger.Println(err)
	}
	// Insert all the results
	for _, g := range c.Groups {
		// Insert a result for each target in the group
		for _, t := range g.Targets {
			r := &Result{Email: t.Email, Position: t.Position, Status: STATUS_SENDING, CampaignId: c.Id, UserId: c.UserId, FirstName: t.FirstName, LastName: t.LastName}
			r.GenerateId()
			err = db.Save(r).Error
			if err != nil {
				Logger.Printf("Error adding result record for target %s\n", t.Email)
				Logger.Println(err)
			}
			c.Results = append(c.Results, *r)
		}
	}
	return nil
}

//DeleteCampaign deletes the specified campaign
func DeleteCampaign(id int64) error {
	Logger.Printf("Deleting campaign %d\n", id)
	// Delete all the campaign results
	err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = db.Where("campaign_id=?", id).Delete(&Event{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	// Delete the campaign
	err = db.Delete(&Campaign{Id: id}).Error
	if err != nil {
		Logger.Panicln(err)
		return err
	}
	return err
}
<commit_msg>Updated GetCampaign(s) to fail gracefully if certain attributes have since been deleted. Fixes #197<commit_after>package models

import (
	"errors"
	"fmt"
	"time"

	"github.com/jinzhu/gorm"
)

//Campaign is a struct representing a created campaign
type Campaign struct {
	Id            int64     `json:"id"`
	UserId        int64     `json:"-"`
	Name          string    `json:"name" sql:"not null"`
	CreatedDate   time.Time `json:"created_date"`
	CompletedDate time.Time `json:"completed_date"`
	TemplateId    int64     `json:"-"`
	Template      Template  `json:"template"`
	PageId        int64     `json:"-"`
	Page          Page      `json:"page"`
	Status        string    `json:"status"`
	Results       []Result  `json:"results,omitempty"`
	Groups        []Group   `json:"groups,omitempty"`
	Events        []Event   `json:"timeline,omitemtpy"`
	SMTPId        int64     `json:"-"`
	SMTP          SMTP      `json:"smtp"`
	URL           string    `json:"url"`
}

// ErrCampaignNameNotSpecified indicates there was no template given by the user
var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified")

// ErrGroupNotSpecified indicates there was no template given by the user
var ErrGroupNotSpecified = errors.New("No groups specified")

// ErrTemplateNotSpecified indicates there was no template given by the user
var ErrTemplateNotSpecified = errors.New("No email template specified")

// ErrPageNotSpecified indicates a landing page was not provided for the campaign
var ErrPageNotSpecified = errors.New("No landing page specified")

// ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign
var ErrSMTPNotSpecified = errors.New("No sending profile specified")

// ErrTemplateNotFound indicates the template specified does not exist in the database
var ErrTemplateNotFound = errors.New("Template not found")

// ErrGroupnNotFound indicates a group specified by the user does not exist in the database
var ErrGroupNotFound = errors.New("Group not found")

// ErrPageNotFound indicates a page specified by the user does not exist in the database
var ErrPageNotFound = errors.New("Page not found")

// ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database
var ErrSMTPNotFound = errors.New("Sending profile not found")

// Validate checks to make sure there are no invalid fields in a submitted campaign
func (c *Campaign) Validate() error {
	switch {
	case c.Name == "":
		return ErrCampaignNameNotSpecified
	case len(c.Groups) == 0:
		return ErrGroupNotSpecified
	case c.Template.Name == "":
		return ErrTemplateNotSpecified
	case c.Page.Name == "":
		return ErrPageNotSpecified
	case c.SMTP.Name == "":
		return ErrSMTPNotSpecified
	}
	return nil
}

// SendTestEmailRequest is the structure of a request
// to send a test email to test an SMTP connection
type SendTestEmailRequest struct {
	Template    Template `json:"template"`
	Page        Page     `json:"page"`
	SMTP        SMTP     `json:"smtp"`
	URL         string   `json:"url"`
	Tracker     string   `json:"tracker"`
	TrackingURL string   `json:"tracking_url"`
	From        string   `json:"from"`
	Target
}

// Validate ensures the SendTestEmailRequest structure
// is valid.
func (s *SendTestEmailRequest) Validate() error {
	switch {
	case s.Email == "":
		return ErrEmailNotSpecified
	}
	return nil
}

// UpdateStatus changes the campaign status appropriately
func (c *Campaign) UpdateStatus(s string) error {
	// This could be made simpler, but I think there's a bug in gorm
	return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
}

// AddEvent creates a new campaign event in the database
func (c *Campaign) AddEvent(e Event) error {
	e.CampaignId = c.Id
	e.Time = time.Now()
	return db.Debug().Save(&e).Error
}

// getDetails retrieves the related attributes of the campaign
// from the database. If the Events and the Results are not available,
// an error is returned. Otherwise, the attribute name is set to [Deleted],
// indicating the user deleted the attribute (template, smtp, etc.)
func (c *Campaign) getDetails() error {
	err = db.Model(c).Related(&c.Results).Error
	if err != nil {
		Logger.Printf("%s: results not found for campaign\n", err)
		return err
	}
	err = db.Model(c).Related(&c.Events).Error
	if err != nil {
		Logger.Printf("%s: events not found for campaign\n", err)
		return err
	}
	err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
	if err != nil {
		if err != gorm.ErrRecordNotFound {
			return err
		}
		c.Template = Template{Name: "[Deleted]"}
		Logger.Printf("%s: template not found for campaign\n", err)
	}
	err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
	if err != nil {
		if err != gorm.ErrRecordNotFound {
			return err
		}
		c.Page = Page{Name: "[Deleted]"}
		Logger.Printf("%s: page not found for campaign\n", err)
	}
	err = db.Table("SMTP").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
	if err != nil {
		// Check if the SMTP was deleted
		if err != gorm.ErrRecordNotFound {
			return err
		}
		c.SMTP = SMTP{Name: "[Deleted]"}
		Logger.Printf("%s: sending profile not found for campaign\n", err)
	}
	return nil
}

// Event contains the fields for an event
// that occurs during the campaign
type Event struct {
	Id         int64     `json:"-"`
	CampaignId int64     `json:"-"`
	Email      string    `json:"email"`
	Time       time.Time `json:"time"`
	Message    string    `json:"message"`
	Details    string    `json:"details"`
}

// GetCampaigns returns the campaigns owned by the given user.
func GetCampaigns(uid int64) ([]Campaign, error) {
	cs := []Campaign{}
	err := db.Model(&User{Id: uid}).Related(&cs).Error
	if err != nil {
		fmt.Println(err)
	}
	for i, _ := range cs {
		err = cs[i].getDetails()
		if err != nil {
			Logger.Println(err)
		}
	}
	return cs, err
}

// GetCampaign returns the campaign, if it exists, specified by the given id and user_id.
func GetCampaign(id int64, uid int64) (Campaign, error) {
	c := Campaign{}
	err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
	if err != nil {
		Logger.Printf("%s: campaign not found\n", err)
		return c, err
	}
	err = c.getDetails()
	return c, err
}

// PostCampaign inserts a campaign and all associated records into the database.
func PostCampaign(c *Campaign, uid int64) error {
	if err := c.Validate(); err != nil {
		return err
	}
	// Fill in the details
	c.UserId = uid
	c.CreatedDate = time.Now()
	c.CompletedDate = time.Time{}
	c.Status = CAMPAIGN_QUEUED
	// Check to make sure all the groups already exist
	for i, g := range c.Groups {
		c.Groups[i], err = GetGroupByName(g.Name, uid)
		if err == gorm.ErrRecordNotFound {
			Logger.Printf("Error - Group %s does not exist", g.Name)
			return ErrGroupNotFound
		} else if err != nil {
			Logger.Println(err)
			return err
		}
	}
	// Check to make sure the template exists
	t, err := GetTemplateByName(c.Template.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Template %s does not exist", t.Name)
		return ErrTemplateNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Template = t
	c.TemplateId = t.Id
	// Check to make sure the page exists
	p, err := GetPageByName(c.Page.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Page %s does not exist", p.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.Page = p
	c.PageId = p.Id
	// Check to make sure the sending profile exists
	s, err := GetSMTPByName(c.SMTP.Name, uid)
	if err == gorm.ErrRecordNotFound {
		Logger.Printf("Error - Sending profile %s does not exist", s.Name)
		return ErrPageNotFound
	} else if err != nil {
		Logger.Println(err)
		return err
	}
	c.SMTP = s
	c.SMTPId = s.Id
	// Insert into the DB
	err = db.Save(c).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = c.AddEvent(Event{Message: "Campaign Created"})
	if err != nil {
		Logger.Println(err)
	}
	// Insert all the results
	for _, g := range c.Groups {
		// Insert a result for each target in the group
		for _, t := range g.Targets {
			r := &Result{Email: t.Email, Position: t.Position, Status: STATUS_SENDING, CampaignId: c.Id, UserId: c.UserId, FirstName: t.FirstName, LastName: t.LastName}
			r.GenerateId()
			err = db.Save(r).Error
			if err != nil {
				Logger.Printf("Error adding result record for target %s\n", t.Email)
				Logger.Println(err)
			}
			c.Results = append(c.Results, *r)
		}
	}
	return nil
}

//DeleteCampaign deletes the specified campaign
func DeleteCampaign(id int64) error {
	Logger.Printf("Deleting campaign %d\n", id)
	// Delete all the campaign results
	err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	err = db.Where("campaign_id=?", id).Delete(&Event{}).Error
	if err != nil {
		Logger.Println(err)
		return err
	}
	// Delete the campaign
	err = db.Delete(&Campaign{Id: id}).Error
	if err != nil {
		Logger.Panicln(err)
		return err
	}
	return err
}
<|endoftext|>


<commit_before>package css_test

import (
	. "github.com/r7kamura/gospel"
	"github.com/ysugimoto/gssp"
	"io/ioutil"
	"os"
	"testing"
)

var cssList = []string{
	"./cases/atrule-decls",
	"./cases/atrule-no-semicolon",
	"./cases/colun-seector",
	"./cases/escape",
	"./cases/important",
	"./cases/rule-no-semicolon",
	"./cases/atrule-no-space",
	"./cases/comments",
	"./cases/extends",
	"./cases/prop-hacks",
	"./cases/selector",
	"./cases/atrule-empty",
	"./cases/atrule-params",
	"./cases/decls",
	"./cases/function",
	"./cases/quotes",
	"./cases/semicolons",
	"./cases/atrule-no-params",
	"./cases/atrule-rules",
	"./cases/empty",
	"./cases/ie-progid",
	"./cases/raw-decl",
	"./cases/tab",
}

func TestParser(t *testing.T) {

	Describe(t, "Test Cases From PostCSS", func() {

		for _, file := range cssList {
			Context(file+".css parse test", func() {
				It(file+".css should be same "+file+".json", func() {
					parser := gssp.NewParser()
					cssfp, _ := os.Open(file + ".css")
					jsonfp, _ := os.Open(file + ".json")

					defer func() {
						cssfp.Close()
						jsonfp.Close()
					}()

					data, _ := ioutil.ReadAll(cssfp)
					jsonData, _ := ioutil.ReadAll(jsonfp)
					actual := parser.Parse(data).ToPrettyJSONString()

					expect := string(jsonData)

					Expect(actual).To(Equal, expect)
				})
			})
		}
	})
}
<commit_msg>Fixed test script: Remove trail \n code (e.g. vim)<commit_after>package css_test

import (
	"bytes"
	. "github.com/r7kamura/gospel"
	"github.com/ysugimoto/gssp"
	"io/ioutil"
	"os"
	"testing"
)

var cssList = []string{
	"./cases/atrule-decls",
	"./cases/atrule-no-semicolon",
	"./cases/colun-seector",
	"./cases/escape",
	"./cases/important",
	"./cases/rule-no-semicolon",
	"./cases/atrule-no-space",
	"./cases/comments",
	"./cases/extends",
	"./cases/prop-hacks",
	"./cases/selector",
	"./cases/atrule-empty",
	"./cases/atrule-params",
	"./cases/decls",
	"./cases/function",
	"./cases/quotes",
	"./cases/semicolons",
	"./cases/atrule-no-params",
	"./cases/atrule-rules",
	"./cases/empty",
	"./cases/ie-progid",
	"./cases/raw-decl",
	"./cases/tab",
}

func TestParser(t *testing.T) {

	Describe(t, "Test Cases From PostCSS", func() {

		for _, file := range cssList {
			Context(file+".css parse test", func() {
				It(file+".css should be same "+file+".json", func() {
					parser := gssp.NewParser()
					cssfp, _ := os.Open(file + ".css")
					jsonfp, _ := os.Open(file + ".json")

					defer func() {
						cssfp.Close()
						jsonfp.Close()
					}()

					data, _ := ioutil.ReadAll(cssfp)
					jsonData, _ := ioutil.ReadAll(jsonfp)
					actual := parser.Parse(data).ToPrettyJSONString()

					// Trim trail \n for vim, ...
					expect := string(bytes.TrimRight(jsonData, "\n"))

					Expect(actual).To(Equal, expect)
				})
			})
		}
	})
}
<|endoftext|>


<commit_before>package accounts

import (
	"strings"
	"time"

	"github.com/rackspace/gophercloud"
)

// UpdateResult is returned from a call to the Update function.
type UpdateResult struct {
	gophercloud.HeaderResult
}

// UpdateHeader represents the headers returned in the response from an Update request.
type UpdateHeader struct {
	ContentLength string    `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans_ID"`
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (ur UpdateResult) Extract() (UpdateHeader, error) {
	var uh UpdateHeader
	if ur.Err != nil {
		return uh, ur.Err
	}

	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
		return uh, err
	}

	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
		if err != nil {
			return uh, err
		}
		uh.Date = t
	}

	return uh, nil
}

// GetHeader represents the headers returned in the response from a Get request.
type GetHeader struct {
	BytesUsed      int64     `mapstructure:"X-Account-Bytes-Used"`
	ContainerCount int       `mapstructure:"X-Account-Container-Count"`
	ContentLength  int64     `mapstructure:"Content-Length"`
	ContentType    string    `mapstructure:"Content-Type"`
	Date           time.Time `mapstructure:"-"`
	ObjectCount    int64     `mapstructure:"X-Account-Object-Count"`
	TransID        string    `mapstructure:"X-Trans-Id"`
}

// GetResult is returned from a call to the Get function.
type GetResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (gr GetResult) Extract() (GetHeader, error) {
	var gh GetHeader
	if gr.Err != nil {
		return gh, gr.Err
	}

	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
		return gh, err
	}

	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
		if err != nil {
			return gh, err
		}
		gh.Date = t
	}

	return gh, nil
}

// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
// and returns the custom metatdata associated with the account.
func (gr GetResult) ExtractMetadata() (map[string]string, error) {
	if gr.Err != nil {
		return nil, gr.Err
	}

	metadata := make(map[string]string)
	for k, v := range gr.Header {
		if strings.HasPrefix(k, "X-Account-Meta-") {
			key := strings.TrimPrefix(k, "X-Account-Meta-")
			metadata[key] = v[0]
		}
	}
	return metadata, nil
}
<commit_msg>struct tag '_' -> '-'<commit_after>package accounts

import (
	"strings"
	"time"

	"github.com/rackspace/gophercloud"
)

// UpdateResult is returned from a call to the Update function.
type UpdateResult struct {
	gophercloud.HeaderResult
}

// UpdateHeader represents the headers returned in the response from an Update request.
type UpdateHeader struct {
	ContentLength string    `mapstructure:"Content-Length"`
	ContentType   string    `mapstructure:"Content-Type"`
	Date          time.Time `mapstructure:"-"`
	TransID       string    `mapstructure:"X-Trans-ID"`
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (ur UpdateResult) Extract() (UpdateHeader, error) {
	var uh UpdateHeader
	if ur.Err != nil {
		return uh, ur.Err
	}

	if err := gophercloud.DecodeHeader(ur.Header, &uh); err != nil {
		return uh, err
	}

	if date, ok := ur.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, ur.Header["Date"][0])
		if err != nil {
			return uh, err
		}
		uh.Date = t
	}

	return uh, nil
}

// GetHeader represents the headers returned in the response from a Get request.
type GetHeader struct {
	BytesUsed      int64     `mapstructure:"X-Account-Bytes-Used"`
	ContainerCount int       `mapstructure:"X-Account-Container-Count"`
	ContentLength  int64     `mapstructure:"Content-Length"`
	ContentType    string    `mapstructure:"Content-Type"`
	Date           time.Time `mapstructure:"-"`
	ObjectCount    int64     `mapstructure:"X-Account-Object-Count"`
	TransID        string    `mapstructure:"X-Trans-Id"`
}

// GetResult is returned from a call to the Get function.
type GetResult struct {
	gophercloud.HeaderResult
}

// Extract will return a struct of headers returned from a call to Get. To obtain
// a map of headers, call the ExtractHeader method on the GetResult.
func (gr GetResult) Extract() (GetHeader, error) {
	var gh GetHeader
	if gr.Err != nil {
		return gh, gr.Err
	}

	if err := gophercloud.DecodeHeader(gr.Header, &gh); err != nil {
		return gh, err
	}

	if date, ok := gr.Header["Date"]; ok && len(date) > 0 {
		t, err := time.Parse(time.RFC1123, gr.Header["Date"][0])
		if err != nil {
			return gh, err
		}
		gh.Date = t
	}

	return gh, nil
}

// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
// and returns the custom metatdata associated with the account.
func (gr GetResult) ExtractMetadata() (map[string]string, error) {
	if gr.Err != nil {
		return nil, gr.Err
	}

	metadata := make(map[string]string)
	for k, v := range gr.Header {
		if strings.HasPrefix(k, "X-Account-Meta-") {
			key := strings.TrimPrefix(k, "X-Account-Meta-")
			metadata[key] = v[0]
		}
	}
	return metadata, nil
}
<|endoftext|>