<commit_before>// Package sp provides tools for buildin an SP such as serving metadata,
// authenticating an assertion and building assertions for IdPs.
package saml

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"net/url"
	"strings"

	"github.com/goware/saml/xmlsec"
	"github.com/pkg/errors"
)

// AuthnRequestURL creates SAML 2.0 AuthnRequest redirect URL,
// aka SP-initiated login (SP->IdP).
// The data is passed in the ?SAMLRequest query parameter and
// the value is base64 encoded and deflate-compressed <AuthnRequest>
// XML element. The final redirect destination that will be invoked
// on successful login is passed using ?RelayState query parameter.
func (sp *ServiceProvider) AuthnRequestURL(relayState string) (string, error) {
	destination, err := sp.GetIdPAuthResource()
	if err != nil {
		return "", errors.Wrap(err, "failed to get IdP destination")
	}

	authnRequest, err := sp.NewAuthnRequest(destination)
	if err != nil {
		return "", errors.Wrapf(err, "failed to make auth request to %v", destination)
	}

	buf, err := xml.MarshalIndent(authnRequest, "", "\t")
	if err != nil {
		return "", errors.Wrap(err, "Failed to marshal auth request")
	}

	flateBuf := bytes.NewBuffer(nil)
	flateWriter, err := flate.NewWriter(flateBuf, flate.DefaultCompression)
	if err != nil {
		return "", errors.Wrap(err, "failed to create flate writer")
	}

	_, err = flateWriter.Write(buf)
	if err != nil {
		return "", errors.Wrap(err, "failed to write to flate writer")
	}
	flateWriter.Close()
	message := base64.StdEncoding.EncodeToString(flateBuf.Bytes())

	redirectURL := destination + fmt.Sprintf(`?RelayState=%s&SAMLRequest=%s`, url.QueryEscape(relayState), url.QueryEscape(message))

	return redirectURL, nil
}

// MetadataXML returns SAML 2.0 Service Provider metadata XML.
func (sp *ServiceProvider) MetadataXML() ([]byte, error) {
	metadata, err := sp.Metadata()
	if err != nil {
		return nil, errors.Wrap(err, "could not build nor serve metadata XML")
	}

	out, err := xml.MarshalIndent(metadata, "", "\t")
	if err != nil {
		return nil, errors.Wrap(err, "could not format metadata")
	}

	return out, nil
}

func (sp *ServiceProvider) possibleResponseIDs() []string {
	responseIDs := []string{}
	if sp.AllowIdpInitiated {
		responseIDs = append(responseIDs, "")
	}
	return responseIDs
}

func (sp *ServiceProvider) verifySignature(plaintextMessage []byte) error {
	idpCertFile, err := sp.GetIdPCertFile()
	if err != nil {
		return err
	}

	err = xmlsec.Verify(plaintextMessage, idpCertFile, &xmlsec.ValidationOptions{
		DTDFile: sp.DTDFile,
	})
	if err == nil {
		// No error, this message is OK
		return nil
	}

	// We got an error...
	if !IsSecurityException(err, &sp.SecurityOpts) {
		// ...but it was not a security exception, so we ignore it and accept
		// the verification.
		return nil
	}

	return err
}

func (sp *ServiceProvider) parseSAMLResponse(samlResponse string) (*Response, error) {
	samlResponseXML, err := base64.StdEncoding.DecodeString(samlResponse)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to base64-decode SAML response")
	}

	var res Response
	err = xml.Unmarshal(samlResponseXML, &res)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to unmarshal XML document: %s", string(samlResponseXML))
	}

	return &res, nil
}

func (sp *ServiceProvider) AssertResponse(samlResponse string) (*Assertion, error) {
	now := Now()

	res, err := sp.parseSAMLResponse(samlResponse)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse SAML response")
	}

	// TODO: Do we really need to check the IdP metadata here?
	if _, err := sp.GetIdPMetadata(); err != nil {
		return nil, errors.Wrap(err, "unable to retrieve IdP metadata")
	}

	// Validate message.

	if res.Destination != sp.AcsURL {
		// Note: OneLogin triggers this error when the Recipient field
		// is left blank (or when not set to the correct ACS endpoint)
		// in the OneLogin SAML configuration page. OneLogin returns
		// Destination="{recipient}" in the SAML reponse in this case.
		return nil, errors.Errorf("Wrong ACS destination, expected %q, got %q", sp.AcsURL, res.Destination)
	}

	if sp.IdPMetadata.EntityID != "" {
		if res.Issuer == nil {
			return nil, errors.New(`Issuer does not match expected entity ID: Missing "Issuer" node`)
		}
		if res.Issuer.Value != sp.IdPMetadata.EntityID {
			return nil, errors.Errorf("Issuer does not match expected entity ID: expected %q, got %q", sp.IdPMetadata.EntityID, res.Issuer.Value)
		}
	}

	if res.Status.StatusCode.Value != "urn:oasis:names:tc:SAML:2.0:status:Success" {
		return nil, errors.Errorf("Unexpected status code: %v", res.Status.StatusCode.Value)
	}

	expectedResponse := false
	responseIDs := sp.possibleResponseIDs()
	for i := range responseIDs {
		if responseIDs[i] == res.InResponseTo {
			expectedResponse = true
		}
	}
	if len(responseIDs) == 1 && responseIDs[0] == "" {
		expectedResponse = true
	}
	if !expectedResponse && len(responseIDs) > 0 {
		return nil, errors.Errorf("Expecting a proper InResponseTo value, got %#v", responseIDs)
	}

	// Try getting the IdP's cert file before using it.
	if _, err := sp.GetIdPCertFile(); err != nil {
		return nil, errors.Wrap(err, "failed to get private key")
	}

	// Validate signatures

	if res.Signature != nil {
		err := validateSignedNode(res.Signature, res.ID)
		if err != nil {
			return nil, errors.Wrap(err, "failed to validate Response + Signature")
		}
	}

	if res.Assertion != nil && res.Assertion.Signature != nil {
		err := validateSignedNode(res.Assertion.Signature, res.Assertion.ID)
		if err != nil {
			return nil, errors.Wrap(err, "failed to validate Assertion + Signature")
		}
	}

	// Validating message.
	signatureOK := false

	if res.Signature != nil || (res.Assertion != nil && res.Assertion.Signature != nil) {
		err := sp.verifySignature([]byte(samlResponse))
		if err != nil {
			return nil, errors.Wrap(err, "Unable to verify message signature")
		} else {
			signatureOK = true
		}
	}

	// Retrieve assertion
	var assertion *Assertion

	if res.EncryptedAssertion != nil {
		keyFile, err := sp.PrivkeyFile()
		if err != nil {
			return nil, errors.Errorf("Failed to get private key: %v", err)
		}

		plainTextAssertion, err := xmlsec.Decrypt(res.EncryptedAssertion.EncryptedData, keyFile)
		if err != nil {
			if IsSecurityException(err, &sp.SecurityOpts) {
				return nil, errors.Wrap(err, "Unable to decrypt message")
			}
		}

		assertion = &Assertion{}
		if err := xml.Unmarshal(plainTextAssertion, assertion); err != nil {
			return nil, errors.Wrap(err, "Unable to parse assertion")
		}

		if assertion.Signature != nil {
			err := validateSignedNode(assertion.Signature, assertion.ID)
			if err != nil {
				return nil, errors.Wrap(err, "failed to validate Assertion + Signature")
			}

			err = sp.verifySignature(plainTextAssertion)
			if err != nil {
				return nil, errors.Wrapf(err, "Unable to verify assertion signature")
			} else {
				signatureOK = true
			}
		}
	} else {
		assertion = res.Assertion
	}
	if assertion == nil {
		return nil, errors.New("Missing assertion")
	}

	// Did we receive a signature?
	if !signatureOK {
		return nil, errors.New("Unable to validate signature: node not found")
	}

	// Validate assertion.
	switch {
	case sp.IdPMetadata.EntityID == "":
		// Skip issuer validation
	case res.Issuer == nil:
		return nil, errors.New(`Assertion issuer does not match expected entity ID: missing Assertion > Issuer`)
	case assertion.Issuer.Value != sp.IdPMetadata.EntityID:
		return nil, errors.Errorf("Assertion issuer does not match expected entity ID: Expected %q, got %q", sp.IdPMetadata.EntityID, assertion.Issuer.Value)
	}

	// Validate recipient
	{
		var err error
		switch {
		case assertion.Subject == nil:
			err = errors.New(`missing Assertion > Subject`)
		case assertion.Subject.SubjectConfirmation == nil:
			err = errors.New(`missing Assertion > Subject > SubjectConfirmation`)
		case assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient != sp.AcsURL:
			err = errors.Errorf("unexpected assertion recipient, expected %q, got %q", sp.AcsURL, assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient)
		}
		if err != nil {
			return nil, errors.Wrapf(err, "invalid assertion recipient")
		}
	}

	// Make sure we have Conditions
	if assertion.Conditions == nil {
		return nil, errors.New(`missing Assertion > Conditions`)
	}

	// The NotBefore and NotOnOrAfter attributes specify time limits on the
	// validity of the assertion within the context of its profile(s) of use.
	// They do not guarantee that the statements in the assertion will be
	// correct or accurate throughout the validity period. The NotBefore
	// attribute specifies the time instant at which the validity interval
	// begins. The NotOnOrAfter attribute specifies the time instant at which
	// the validity interval has ended. If the value for either NotBefore or
	// NotOnOrAfter is omitted, then it is considered unspecified.
	{
		validFrom := assertion.Conditions.NotBefore
		if !validFrom.IsZero() && validFrom.After(now.Add(ClockDriftTolerance)) {
			return nil, errors.Errorf("Assertion conditions are not valid yet, got %v, current time is %v", validFrom, now)
		}
	}

	{
		validUntil := assertion.Conditions.NotOnOrAfter
		if !validUntil.IsZero() && validUntil.Before(now.Add(-ClockDriftTolerance)) {
			return nil, errors.Errorf("Assertion conditions already expired, got %v current time is %v, extra time is %v", validUntil, now, now.Add(-ClockDriftTolerance))
		}
	}

	// A time instant at which the subject can no longer be confirmed. The time
	// value is encoded in UTC, as described in Section 1.3.3.
	//
	// Note that the time period specified by the optional NotBefore and
	// NotOnOrAfter attributes, if present, SHOULD fall within the overall
	// assertion validity period as specified by the element's NotBefore and
	// NotOnOrAfter attributes. If both attributes are present, the value for
	// NotBefore MUST be less than (earlier than) the value for NotOnOrAfter.

	if validUntil := assertion.Subject.SubjectConfirmation.SubjectConfirmationData.NotOnOrAfter; validUntil.Before(now.Add(-ClockDriftTolerance)) {
		err := errors.Errorf("Assertion conditions already expired, got %v current time is %v", validUntil, now)
		return nil, errors.Wrap(err, "Assertion conditions already expired")
	}

	// if assertion.Conditions != nil && assertion.Conditions.AudienceRestriction != nil {
	//   if assertion.Conditions.AudienceRestriction.Audience.Value != sp.MetadataURL {
	//     returnt.Errorf("Audience restriction mismatch, got %q, expected %q", assertion.Conditions.AudienceRestriction.Audience.Value, sp.MetadataURL), errors.New("Audience restriction mismatch")
	//   }
	// }

	expectedResponse = false
	for i := range responseIDs {
		if responseIDs[i] == assertion.Subject.SubjectConfirmation.SubjectConfirmationData.InResponseTo {
			expectedResponse = true
		}
	}
	if len(responseIDs) == 1 && responseIDs[0] == "" {
		expectedResponse = true
	}

	if !expectedResponse && len(responseIDs) > 0 {
		return nil, errors.New("Unexpected assertion InResponseTo value")
	}

	return assertion, nil
}

func validateSignedNode(signature *xmlsec.Signature, nodeID string) error {
	signatureURI := signature.Reference.URI
	if signatureURI == "" {
		return nil
	}
	if strings.HasPrefix(signatureURI, "#") {
		if nodeID == signatureURI[1:] {
			return nil
		}
		return fmt.Errorf("signed Reference.URI %q does not match ID %v", signatureURI, nodeID)
	}
	return fmt.Errorf("cannot lookup external URIs (%q)", signatureURI)
}
<commit_msg>Verify signature of decoded response<commit_after>// Package sp provides tools for buildin an SP such as serving metadata,
// authenticating an assertion and building assertions for IdPs.
package saml

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"net/url"
	"strings"

	"github.com/goware/saml/xmlsec"
	"github.com/pkg/errors"
)

// AuthnRequestURL creates SAML 2.0 AuthnRequest redirect URL,
// aka SP-initiated login (SP->IdP).
// The data is passed in the ?SAMLRequest query parameter and
// the value is base64 encoded and deflate-compressed <AuthnRequest>
// XML element. The final redirect destination that will be invoked
// on successful login is passed using ?RelayState query parameter.
func (sp *ServiceProvider) AuthnRequestURL(relayState string) (string, error) {
	destination, err := sp.GetIdPAuthResource()
	if err != nil {
		return "", errors.Wrap(err, "failed to get IdP destination")
	}

	authnRequest, err := sp.NewAuthnRequest(destination)
	if err != nil {
		return "", errors.Wrapf(err, "failed to make auth request to %v", destination)
	}

	buf, err := xml.MarshalIndent(authnRequest, "", "\t")
	if err != nil {
		return "", errors.Wrap(err, "Failed to marshal auth request")
	}

	flateBuf := bytes.NewBuffer(nil)
	flateWriter, err := flate.NewWriter(flateBuf, flate.DefaultCompression)
	if err != nil {
		return "", errors.Wrap(err, "failed to create flate writer")
	}

	_, err = flateWriter.Write(buf)
	if err != nil {
		return "", errors.Wrap(err, "failed to write to flate writer")
	}
	flateWriter.Close()
	message := base64.StdEncoding.EncodeToString(flateBuf.Bytes())

	redirectURL := destination + fmt.Sprintf(`?RelayState=%s&SAMLRequest=%s`, url.QueryEscape(relayState), url.QueryEscape(message))

	return redirectURL, nil
}

// MetadataXML returns SAML 2.0 Service Provider metadata XML.
func (sp *ServiceProvider) MetadataXML() ([]byte, error) {
	metadata, err := sp.Metadata()
	if err != nil {
		return nil, errors.Wrap(err, "could not build nor serve metadata XML")
	}

	out, err := xml.MarshalIndent(metadata, "", "\t")
	if err != nil {
		return nil, errors.Wrap(err, "could not format metadata")
	}

	return out, nil
}

func (sp *ServiceProvider) possibleResponseIDs() []string {
	responseIDs := []string{}
	if sp.AllowIdpInitiated {
		responseIDs = append(responseIDs, "")
	}
	return responseIDs
}

func (sp *ServiceProvider) verifySignature(plaintextMessage []byte) error {
	idpCertFile, err := sp.GetIdPCertFile()
	if err != nil {
		return err
	}

	err = xmlsec.Verify(plaintextMessage, idpCertFile, &xmlsec.ValidationOptions{
		DTDFile: sp.DTDFile,
	})
	if err == nil {
		// No error, this message is OK
		return nil
	}

	// We got an error...
	if !IsSecurityException(err, &sp.SecurityOpts) {
		// ...but it was not a security exception, so we ignore it and accept
		// the verification.
		return nil
	}

	return err
}

func (sp *ServiceProvider) AssertResponse(samlResponse string) (*Assertion, error) {
	now := Now()

	samlResponseXML, err := base64.StdEncoding.DecodeString(samlResponse)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to base64-decode SAML response")
	}

	var res Response
	err = xml.Unmarshal(samlResponseXML, &res)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to unmarshal XML document: %s", string(samlResponseXML))
	}

	// TODO: Do we really need to check the IdP metadata here?
	if _, err := sp.GetIdPMetadata(); err != nil {
		return nil, errors.Wrap(err, "unable to retrieve IdP metadata")
	}

	// Validate message.

	if res.Destination != sp.AcsURL {
		// Note: OneLogin triggers this error when the Recipient field
		// is left blank (or when not set to the correct ACS endpoint)
		// in the OneLogin SAML configuration page. OneLogin returns
		// Destination="{recipient}" in the SAML reponse in this case.
		return nil, errors.Errorf("Wrong ACS destination, expected %q, got %q", sp.AcsURL, res.Destination)
	}

	if sp.IdPMetadata.EntityID != "" {
		if res.Issuer == nil {
			return nil, errors.New(`Issuer does not match expected entity ID: Missing "Issuer" node`)
		}
		if res.Issuer.Value != sp.IdPMetadata.EntityID {
			return nil, errors.Errorf("Issuer does not match expected entity ID: expected %q, got %q", sp.IdPMetadata.EntityID, res.Issuer.Value)
		}
	}

	if res.Status.StatusCode.Value != "urn:oasis:names:tc:SAML:2.0:status:Success" {
		return nil, errors.Errorf("Unexpected status code: %v", res.Status.StatusCode.Value)
	}

	expectedResponse := false
	responseIDs := sp.possibleResponseIDs()
	for i := range responseIDs {
		if responseIDs[i] == res.InResponseTo {
			expectedResponse = true
		}
	}
	if len(responseIDs) == 1 && responseIDs[0] == "" {
		expectedResponse = true
	}
	if !expectedResponse && len(responseIDs) > 0 {
		return nil, errors.Errorf("Expecting a proper InResponseTo value, got %#v", responseIDs)
	}

	// Try getting the IdP's cert file before using it.
	if _, err := sp.GetIdPCertFile(); err != nil {
		return nil, errors.Wrap(err, "failed to get private key")
	}

	// Validate signatures

	if res.Signature != nil {
		err := validateSignedNode(res.Signature, res.ID)
		if err != nil {
			return nil, errors.Wrap(err, "failed to validate Response + Signature")
		}
	}

	if res.Assertion != nil && res.Assertion.Signature != nil {
		err := validateSignedNode(res.Assertion.Signature, res.Assertion.ID)
		if err != nil {
			return nil, errors.Wrap(err, "failed to validate Assertion + Signature")
		}
	}

	// Validating message.
	signatureOK := false

	if res.Signature != nil || (res.Assertion != nil && res.Assertion.Signature != nil) {
		err := sp.verifySignature(samlResponseXML)
		if err != nil {
			return nil, errors.Wrap(err, "Unable to verify message signature")
		} else {
			signatureOK = true
		}
	}

	// Retrieve assertion
	var assertion *Assertion

	if res.EncryptedAssertion != nil {
		keyFile, err := sp.PrivkeyFile()
		if err != nil {
			return nil, errors.Errorf("Failed to get private key: %v", err)
		}

		plainTextAssertion, err := xmlsec.Decrypt(res.EncryptedAssertion.EncryptedData, keyFile)
		if err != nil {
			if IsSecurityException(err, &sp.SecurityOpts) {
				return nil, errors.Wrap(err, "Unable to decrypt message")
			}
		}

		assertion = &Assertion{}
		if err := xml.Unmarshal(plainTextAssertion, assertion); err != nil {
			return nil, errors.Wrap(err, "Unable to parse assertion")
		}

		if assertion.Signature != nil {
			err := validateSignedNode(assertion.Signature, assertion.ID)
			if err != nil {
				return nil, errors.Wrap(err, "failed to validate Assertion + Signature")
			}

			err = sp.verifySignature(plainTextAssertion)
			if err != nil {
				return nil, errors.Wrapf(err, "Unable to verify assertion signature")
			} else {
				signatureOK = true
			}
		}
	} else {
		assertion = res.Assertion
	}
	if assertion == nil {
		return nil, errors.New("Missing assertion")
	}

	// Did we receive a signature?
	if !signatureOK {
		return nil, errors.New("Unable to validate signature: node not found")
	}

	// Validate assertion.
	switch {
	case sp.IdPMetadata.EntityID == "":
		// Skip issuer validation
	case res.Issuer == nil:
		return nil, errors.New(`Assertion issuer does not match expected entity ID: missing Assertion > Issuer`)
	case assertion.Issuer.Value != sp.IdPMetadata.EntityID:
		return nil, errors.Errorf("Assertion issuer does not match expected entity ID: Expected %q, got %q", sp.IdPMetadata.EntityID, assertion.Issuer.Value)
	}

	// Validate recipient
	{
		var err error
		switch {
		case assertion.Subject == nil:
			err = errors.New(`missing Assertion > Subject`)
		case assertion.Subject.SubjectConfirmation == nil:
			err = errors.New(`missing Assertion > Subject > SubjectConfirmation`)
		case assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient != sp.AcsURL:
			err = errors.Errorf("unexpected assertion recipient, expected %q, got %q", sp.AcsURL, assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient)
		}
		if err != nil {
			return nil, errors.Wrapf(err, "invalid assertion recipient")
		}
	}

	// Make sure we have Conditions
	if assertion.Conditions == nil {
		return nil, errors.New(`missing Assertion > Conditions`)
	}

	// The NotBefore and NotOnOrAfter attributes specify time limits on the
	// validity of the assertion within the context of its profile(s) of use.
	// They do not guarantee that the statements in the assertion will be
	// correct or accurate throughout the validity period. The NotBefore
	// attribute specifies the time instant at which the validity interval
	// begins. The NotOnOrAfter attribute specifies the time instant at which
	// the validity interval has ended. If the value for either NotBefore or
	// NotOnOrAfter is omitted, then it is considered unspecified.
	{
		validFrom := assertion.Conditions.NotBefore
		if !validFrom.IsZero() && validFrom.After(now.Add(ClockDriftTolerance)) {
			return nil, errors.Errorf("Assertion conditions are not valid yet, got %v, current time is %v", validFrom, now)
		}
	}

	{
		validUntil := assertion.Conditions.NotOnOrAfter
		if !validUntil.IsZero() && validUntil.Before(now.Add(-ClockDriftTolerance)) {
			return nil, errors.Errorf("Assertion conditions already expired, got %v current time is %v, extra time is %v", validUntil, now, now.Add(-ClockDriftTolerance))
		}
	}

	// A time instant at which the subject can no longer be confirmed. The time
	// value is encoded in UTC, as described in Section 1.3.3.
	//
	// Note that the time period specified by the optional NotBefore and
	// NotOnOrAfter attributes, if present, SHOULD fall within the overall
	// assertion validity period as specified by the element's NotBefore and
	// NotOnOrAfter attributes. If both attributes are present, the value for
	// NotBefore MUST be less than (earlier than) the value for NotOnOrAfter.

	if validUntil := assertion.Subject.SubjectConfirmation.SubjectConfirmationData.NotOnOrAfter; validUntil.Before(now.Add(-ClockDriftTolerance)) {
		err := errors.Errorf("Assertion conditions already expired, got %v current time is %v", validUntil, now)
		return nil, errors.Wrap(err, "Assertion conditions already expired")
	}

	// if assertion.Conditions != nil && assertion.Conditions.AudienceRestriction != nil {
	//   if assertion.Conditions.AudienceRestriction.Audience.Value != sp.MetadataURL {
	//     returnt.Errorf("Audience restriction mismatch, got %q, expected %q", assertion.Conditions.AudienceRestriction.Audience.Value, sp.MetadataURL), errors.New("Audience restriction mismatch")
	//   }
	// }

	expectedResponse = false
	for i := range responseIDs {
		if responseIDs[i] == assertion.Subject.SubjectConfirmation.SubjectConfirmationData.InResponseTo {
			expectedResponse = true
		}
	}
	if len(responseIDs) == 1 && responseIDs[0] == "" {
		expectedResponse = true
	}

	if !expectedResponse && len(responseIDs) > 0 {
		return nil, errors.New("Unexpected assertion InResponseTo value")
	}

	return assertion, nil
}

func validateSignedNode(signature *xmlsec.Signature, nodeID string) error {
	signatureURI := signature.Reference.URI
	if signatureURI == "" {
		return nil
	}
	if strings.HasPrefix(signatureURI, "#") {
		if nodeID == signatureURI[1:] {
			return nil
		}
		return fmt.Errorf("signed Reference.URI %q does not match ID %v", signatureURI, nodeID)
	}
	return fmt.Errorf("cannot lookup external URIs (%q)", signatureURI)
}
<|endoftext|>


<commit_before>package space

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/xchapter7x/lo"
)

// UserMgr - interface type encapsulating Update space users behavior
type UserMgr interface {
	UpdateSpaceUsers(config *ldap.Config, uaaUsers map[string]string, updateUsersInput UpdateUsersInput) error
}

// NewUserManager -
func NewUserManager(
	cloudController cloudcontroller.Manager,
	ldapMgr ldap.Manager,
	uaaMgr uaa.Manager) UserMgr {
	return &UserManager{
		cloudController: cloudController,
		LdapMgr:         ldapMgr,
		uaaMgr:          uaaMgr,
	}
}

type UserManager struct {
	cloudController cloudcontroller.Manager
	LdapMgr         ldap.Manager
	uaaMgr          uaa.Manager
}

// UpdateSpaceUserInput
type UpdateUsersInput struct {
	SpaceGUID                                   string
	OrgGUID                                     string
	Role                                        string
	LdapUsers, Users, LdapGroupNames, SamlUsers []string
	SpaceName                                   string
	OrgName                                     string
	RemoveUsers                                 bool
}

//UpdateSpaceUsers Update space users
func (m *UserManager) UpdateSpaceUsers(config *ldap.Config, uaaUsers map[string]string, updateUsersInput UpdateUsersInput) error {
	spaceUsers, err := m.cloudController.GetCFUsers(updateUsersInput.SpaceGUID, SPACES, updateUsersInput.Role)
	if err != nil {
		return err
	}

	lo.G.Debugf("SpaceUsers before: %v", spaceUsers)
	if config.Enabled {
		var ldapUsers []ldap.User
		ldapUsers, err = m.getLdapUsers(config, updateUsersInput.LdapGroupNames, updateUsersInput.LdapUsers)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %v", ldapUsers)
		for _, user := range ldapUsers {
			err = m.updateLdapUser(config, updateUsersInput.SpaceGUID, updateUsersInput.OrgGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName, uaaUsers, user, spaceUsers)
			if err != nil {
				return err
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	for _, userID := range updateUsersInput.Users {
		lowerUserID := strings.ToLower(userID)
		if _, userExists := uaaUsers[lowerUserID]; !userExists {
			return fmt.Errorf("user %s doesn't exist in cloud foundry, so must add internal user first", lowerUserID)
		}
		if _, ok := spaceUsers[lowerUserID]; !ok {
			if err = m.addUserToOrgAndRole(userID, updateUsersInput.OrgGUID, updateUsersInput.SpaceGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName); err != nil {
				lo.G.Error(err)
				return err
			}
		} else {
			delete(spaceUsers, lowerUserID)
		}
	}

	for _, userEmail := range updateUsersInput.SamlUsers {
		lowerUserEmail := strings.ToLower(userEmail)
		if _, userExists := uaaUsers[lowerUserEmail]; !userExists {
			lo.G.Debug("User", userEmail, "doesn't exist in cloud foundry, so creating user")
			if err = m.uaaMgr.CreateExternalUser(userEmail, userEmail, userEmail, config.Origin); err != nil {
				lo.G.Errorf("Unable to create user [%s] due to error %s", userEmail, err.Error())
				return err
			} else {
				uaaUsers[userEmail] = userEmail
			}
		}
		if _, ok := spaceUsers[lowerUserEmail]; !ok {
			if err = m.addUserToOrgAndRole(userEmail, updateUsersInput.OrgGUID, updateUsersInput.SpaceGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName); err != nil {
				lo.G.Error(err)
				return err
			}
		} else {
			delete(spaceUsers, lowerUserEmail)
		}
	}
	if updateUsersInput.RemoveUsers {
		lo.G.Debugf("Deleting users for org/space: %s/%s", updateUsersInput.OrgName, updateUsersInput.SpaceName)
		for spaceUser, spaceUserGUID := range spaceUsers {
			lo.G.Infof("removing user: %s from space: %s and role: %s", spaceUser, updateUsersInput.SpaceName, updateUsersInput.Role)
			err = m.cloudController.RemoveCFUser(updateUsersInput.SpaceGUID, SPACES, spaceUserGUID, updateUsersInput.Role)
			if err != nil {
				lo.G.Errorf("Unable to remove user : %s from space %s role in space : %s", spaceUser, updateUsersInput.Role, updateUsersInput.SpaceName)
				lo.G.Errorf("Cloud controller API error: %s", err)
				return err
			}
		}
	} else {
		lo.G.Debugf("Not removing users. Set enable-remove-users: true to spaceConfig for org/space: %s/%s", updateUsersInput.OrgName, updateUsersInput.SpaceName)
	}

	lo.G.Debugf("SpaceUsers after: %v", spaceUsers)
	return nil
}

func (m *UserManager) updateLdapUser(config *ldap.Config, spaceGUID, orgGUID string,
	role string, orgName, spaceName string, uaaUsers map[string]string,
	user ldap.User, spaceUsers map[string]string) error {

	userID := user.UserID
	externalID := user.UserDN
	if config.Origin != "ldap" {
		userID = user.Email
		externalID = user.Email
	} else {
		if user.Email == "" {
			user.Email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
		}
	}
	userID = strings.ToLower(userID)

	if _, ok := spaceUsers[userID]; !ok {
		lo.G.Debugf("User[%s] not found in: %v", userID, spaceUsers)
		if _, userExists := uaaUsers[userID]; !userExists {
			lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
			if err := m.uaaMgr.CreateExternalUser(userID, user.Email, externalID, config.Origin); err != nil {
				lo.G.Errorf("Unable to create user [%s] due to error %s", userID, err.Error())
			} else {
				uaaUsers[userID] = userID
				if err := m.addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName); err != nil {
					return err
				}
			}
		} else {
			if err := m.addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName); err != nil {
				return err
			}
		}
	} else {
		delete(spaceUsers, userID)
	}
	return nil
}

func (m *UserManager) getLdapUsers(config *ldap.Config, groupNames []string, userList []string) ([]ldap.User, error) {
	users := []ldap.User{}
	for _, groupName := range groupNames {
		if groupName != "" {
			lo.G.Debug("Finding LDAP user for group:", groupName)
			if groupUsers, err := m.LdapMgr.GetUserIDs(config, groupName); err == nil {
				users = append(users, groupUsers...)
			} else {
				lo.G.Warning(err)
			}
		}
	}
	for _, user := range userList {
		if ldapUser, err := m.LdapMgr.GetUser(config, user); err == nil {
			if ldapUser != nil {
				users = append(users, *ldapUser)
			}
		} else {
			lo.G.Warning(err)
		}
	}
	return users, nil
}

func (m *UserManager) addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName string) error {
	if err := m.cloudController.AddUserToOrg(userID, orgGUID); err != nil {
		lo.G.Error(err)
		return err
	}
	lo.G.Infof("Adding user: %s to org/space: %s/%s with role: %s", userID, orgName, spaceName, role)
	if err := m.cloudController.AddUserToSpaceRole(userID, role, spaceGUID); err != nil {
		lo.G.Error(err)
		return err
	}
	return nil
}
<commit_msg>adding logic to prevent duplicate users from being aggregated together from ldap groups/users list<commit_after>package space

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/xchapter7x/lo"
)

// UserMgr - interface type encapsulating Update space users behavior
type UserMgr interface {
	UpdateSpaceUsers(config *ldap.Config, uaaUsers map[string]string, updateUsersInput UpdateUsersInput) error
}

// NewUserManager -
func NewUserManager(
	cloudController cloudcontroller.Manager,
	ldapMgr ldap.Manager,
	uaaMgr uaa.Manager) UserMgr {
	return &UserManager{
		cloudController: cloudController,
		LdapMgr:         ldapMgr,
		uaaMgr:          uaaMgr,
	}
}

type UserManager struct {
	cloudController cloudcontroller.Manager
	LdapMgr         ldap.Manager
	uaaMgr          uaa.Manager
}

// UpdateSpaceUserInput
type UpdateUsersInput struct {
	SpaceGUID                                   string
	OrgGUID                                     string
	Role                                        string
	LdapUsers, Users, LdapGroupNames, SamlUsers []string
	SpaceName                                   string
	OrgName                                     string
	RemoveUsers                                 bool
}

//UpdateSpaceUsers Update space users
func (m *UserManager) UpdateSpaceUsers(config *ldap.Config, uaaUsers map[string]string, updateUsersInput UpdateUsersInput) error {
	spaceUsers, err := m.cloudController.GetCFUsers(updateUsersInput.SpaceGUID, SPACES, updateUsersInput.Role)
	if err != nil {
		return err
	}

	lo.G.Debugf("SpaceUsers before: %v", spaceUsers)
	if config.Enabled {
		var ldapUsers []ldap.User
		ldapUsers, err = m.getLdapUsers(config, updateUsersInput.LdapGroupNames, updateUsersInput.LdapUsers)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %v", ldapUsers)
		for _, user := range ldapUsers {
			err = m.updateLdapUser(config, updateUsersInput.SpaceGUID, updateUsersInput.OrgGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName, uaaUsers, user, spaceUsers)
			if err != nil {
				return err
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	for _, userID := range updateUsersInput.Users {
		lowerUserID := strings.ToLower(userID)
		if _, userExists := uaaUsers[lowerUserID]; !userExists {
			return fmt.Errorf("user %s doesn't exist in cloud foundry, so must add internal user first", lowerUserID)
		}
		if _, ok := spaceUsers[lowerUserID]; !ok {
			if err = m.addUserToOrgAndRole(userID, updateUsersInput.OrgGUID, updateUsersInput.SpaceGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName); err != nil {
				lo.G.Error(err)
				return err
			}
		} else {
			delete(spaceUsers, lowerUserID)
		}
	}

	for _, userEmail := range updateUsersInput.SamlUsers {
		lowerUserEmail := strings.ToLower(userEmail)
		if _, userExists := uaaUsers[lowerUserEmail]; !userExists {
			lo.G.Debug("User", userEmail, "doesn't exist in cloud foundry, so creating user")
			if err = m.uaaMgr.CreateExternalUser(userEmail, userEmail, userEmail, config.Origin); err != nil {
				lo.G.Errorf("Unable to create user [%s] due to error %s", userEmail, err.Error())
				return err
			} else {
				uaaUsers[userEmail] = userEmail
			}
		}
		if _, ok := spaceUsers[lowerUserEmail]; !ok {
			if err = m.addUserToOrgAndRole(userEmail, updateUsersInput.OrgGUID, updateUsersInput.SpaceGUID, updateUsersInput.Role, updateUsersInput.OrgName, updateUsersInput.SpaceName); err != nil {
				lo.G.Error(err)
				return err
			}
		} else {
			delete(spaceUsers, lowerUserEmail)
		}
	}
	if updateUsersInput.RemoveUsers {
		lo.G.Debugf("Deleting users for org/space: %s/%s", updateUsersInput.OrgName, updateUsersInput.SpaceName)
		for spaceUser, spaceUserGUID := range spaceUsers {
			lo.G.Infof("removing user: %s from space: %s and role: %s", spaceUser, updateUsersInput.SpaceName, updateUsersInput.Role)
			err = m.cloudController.RemoveCFUser(updateUsersInput.SpaceGUID, SPACES, spaceUserGUID, updateUsersInput.Role)
			if err != nil {
				lo.G.Errorf("Unable to remove user : %s from space %s role in space : %s", spaceUser, updateUsersInput.Role, updateUsersInput.SpaceName)
				lo.G.Errorf("Cloud controller API error: %s", err)
				return err
			}
		}
	} else {
		lo.G.Debugf("Not removing users. Set enable-remove-users: true to spaceConfig for org/space: %s/%s", updateUsersInput.OrgName, updateUsersInput.SpaceName)
	}

	lo.G.Debugf("SpaceUsers after: %v", spaceUsers)
	return nil
}

func (m *UserManager) updateLdapUser(config *ldap.Config, spaceGUID, orgGUID string,
	role string, orgName, spaceName string, uaaUsers map[string]string,
	user ldap.User, spaceUsers map[string]string) error {

	userID := user.UserID
	externalID := user.UserDN
	if config.Origin != "ldap" {
		userID = user.Email
		externalID = user.Email
	} else {
		if user.Email == "" {
			user.Email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
		}
	}
	userID = strings.ToLower(userID)

	if _, ok := spaceUsers[userID]; !ok {
		lo.G.Debugf("User[%s] not found in: %v", userID, spaceUsers)
		if _, userExists := uaaUsers[userID]; !userExists {
			lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
			if err := m.uaaMgr.CreateExternalUser(userID, user.Email, externalID, config.Origin); err != nil {
				lo.G.Errorf("Unable to create user [%s] due to error %s", userID, err.Error())
			} else {
				uaaUsers[userID] = userID
				if err := m.addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName); err != nil {
					return err
				}
			}
		} else {
			if err := m.addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName); err != nil {
				return err
			}
		}
	} else {
		delete(spaceUsers, userID)
	}
	return nil
}

func (m *UserManager) getLdapUsers(config *ldap.Config, groupNames []string, userList []string) ([]ldap.User, error) {
	uniqueUsers := make(map[string]string)
	users := []ldap.User{}
	for _, groupName := range groupNames {
		if groupName != "" {
			lo.G.Debug("Finding LDAP user for group:", groupName)
			if groupUsers, err := m.LdapMgr.GetUserIDs(config, groupName); err == nil {
				for _, user := range groupUsers {
					if _, ok := uniqueUsers[user.Email]; !ok {
						users = append(users, user)
					} else {
						lo.G.Debugf("User %v is already added to list", user)
					}
				}
			} else {
				lo.G.Warning(err)
			}
		}
	}
	for _, user := range userList {
		if ldapUser, err := m.LdapMgr.GetUser(config, user); err == nil {
			if ldapUser != nil {
				if _, ok := uniqueUsers[ldapUser.Email]; !ok {
					users = append(users, *ldapUser)
				} else {
					lo.G.Debugf("User %v is already added to list", ldapUser)
				}
			}
		} else {
			lo.G.Warning(err)
		}
	}
	return users, nil
}

func (m *UserManager) addUserToOrgAndRole(userID, orgGUID, spaceGUID, role, orgName, spaceName string) error {
	if err := m.cloudController.AddUserToOrg(userID, orgGUID); err != nil {
		lo.G.Error(err)
		return err
	}
	lo.G.Infof("Adding user: %s to org/space: %s/%s with role: %s", userID, orgName, spaceName, role)
	if err := m.cloudController.AddUserToSpaceRole(userID, role, spaceGUID); err != nil {
		lo.G.Error(err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package routes

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
	"github.com/petergtz/bitsgo/logger"
	"github.com/uber-go/zap"
)

type ResourceHandler struct {
	blobstore    Blobstore
	resourceType string
}

func (handler *ResourceHandler) Put(responseWriter http.ResponseWriter, request *http.Request) {
	var (
		redirectLocation string
		e                error
	)
	if strings.Contains(request.Header.Get("Content-Type"), "multipart/form-data") {
		logger.From(request).Info("Multipart upload")
		file, _, e := request.FormFile(handler.resourceType)
		if e != nil {
			badRequest(responseWriter, "Could not retrieve '%s' form parameter", handler.resourceType)
			return
		}
		defer file.Close()

		redirectLocation, e = handler.blobstore.Put(mux.Vars(request)["identifier"], file)
	} else {
		logger.From(request).Info("Copy source guid")
		redirectLocation, e = handler.copySourceGuid(request.Body, mux.Vars(request)["identifier"], responseWriter)
	}

	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}

	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}

	responseWriter.WriteHeader(http.StatusCreated)
}

func (handler *ResourceHandler) copySourceGuid(body io.ReadCloser, targetGuid string, responseWriter http.ResponseWriter) (string, error) {
	if body == nil {
		badRequest(responseWriter, "Body must contain source_guid when request is not multipart/form-data")
		return "", nil
	}
	defer body.Close()
	content, e := ioutil.ReadAll(body)
	if e != nil {
		internalServerError(responseWriter, e)
		return "", nil
	}
	var payload struct {
		SourceGuid string `json:"source_guid"`
	}
	e = json.Unmarshal(content, &payload)
	if e != nil {
		badRequest(responseWriter, "Body must be valid JSON when request is not multipart/form-data. %+v", e)
		return "", nil
	}
	return handler.blobstore.Copy(payload.SourceGuid, targetGuid)
}

func (handler *ResourceHandler) Head(responseWriter http.ResponseWriter, request *http.Request) {
	body, redirectLocation, e := handler.blobstore.Get(mux.Vars(request)["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	defer body.Close()
	responseWriter.WriteHeader(http.StatusOK)
}

func (handler *ResourceHandler) Get(responseWriter http.ResponseWriter, request *http.Request) {
	body, redirectLocation, e := handler.blobstore.Get(mux.Vars(request)["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	defer body.Close()
	responseWriter.WriteHeader(http.StatusOK)
	io.Copy(responseWriter, body)
}

func (handler *ResourceHandler) Delete(responseWriter http.ResponseWriter, request *http.Request) {
	exists, e := handler.blobstore.Exists(mux.Vars(request)["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	if !exists {
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	}

	e = handler.blobstore.Delete(mux.Vars(request)["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func (handler *ResourceHandler) DeleteDir(responseWriter http.ResponseWriter, request *http.Request) {
	e := handler.blobstore.DeletePrefix(mux.Vars(request)["identifier"])
	if e != nil {
		writeResponseBasedOnError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func writeResponseBasedOnError(responseWriter http.ResponseWriter, e error) {
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNoContent)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func redirect(responseWriter http.ResponseWriter, redirectLocation string) {
	responseWriter.Header().Set("Location", redirectLocation)
	responseWriter.WriteHeader(http.StatusFound)
}

func internalServerError(responseWriter http.ResponseWriter, e error) {
	logger.Log.Error("Internal Server Error.", zap.String("error", fmt.Sprintf("%+v", e)))
	responseWriter.WriteHeader(http.StatusInternalServerError)
}

func badRequest(responseWriter http.ResponseWriter, message string, args ...interface{}) {
	responseWriter.WriteHeader(http.StatusBadRequest)
	fmt.Fprintf(responseWriter, message, args...)
}
<commit_msg>Refactor ResourceHandler Put method<commit_after>package routes

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
	"github.com/petergtz/bitsgo/logger"
	"github.com/uber-go/zap"
)

type ResourceHandler struct {
	blobstore    Blobstore
	resourceType string
}

func (handler *ResourceHandler) Put(responseWriter http.ResponseWriter, request *http.Request) {
	if strings.Contains(request.Header.Get("Content-Type"), "multipart/form-data") {
		logger.From(request).Debug("Multipart upload")
		handler.uploadMultipart(responseWriter, request)
	} else {
		logger.From(request).Debug("Copy source guid")
		handler.copySourceGuid(responseWriter, request)
	}
}

func (handler *ResourceHandler) uploadMultipart(responseWriter http.ResponseWriter, request *http.Request) {
	file, _, e := request.FormFile(handler.resourceType)
	if e != nil {
		badRequest(responseWriter, "Could not retrieve '%s' form parameter", handler.resourceType)
		return
	}
	defer file.Close()

	redirectLocation, e := handler.blobstore.Put(mux.Vars(request)["identifier"], file)
	handleBlobstoreResult(redirectLocation, e, responseWriter)
}

func (handler *ResourceHandler) copySourceGuid(responseWriter http.ResponseWriter, request *http.Request) {
	if request.Body == nil {
		badRequest(responseWriter, "Body must contain source_guid when request is not multipart/form-data")
		return
	}
	sourceGuid := sourceGuidFrom(request.Body, responseWriter)
	if sourceGuid == "" {
		return // response is already handled in sourceGuidFrom
	}
	redirectLocation, e := handler.blobstore.Copy(sourceGuid, mux.Vars(request)["identifier"])
	if _, isNotFoundError := e.(*NotFoundError); isNotFoundError {
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	}
	handleBlobstoreResult(redirectLocation, e, responseWriter)
}

func sourceGuidFrom(body io.ReadCloser, responseWriter http.ResponseWriter) string {
	defer body.Close()
	content, e := ioutil.ReadAll(body)
	if e != nil {
		internalServerError(responseWriter, e)
		return ""
	}
	var payload struct {
		SourceGuid string `json:"source_guid"`
	}
	e = json.Unmarshal(content, &payload)
	if e != nil {
		badRequest(responseWriter, "Body must be valid JSON when request is not multipart/form-data. %+v", e)
		return ""
	}
	return payload.SourceGuid
}

func handleBlobstoreResult(redirectLocation string, e error, responseWriter http.ResponseWriter) {
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}

	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}

	responseWriter.WriteHeader(http.StatusCreated)
}

func (handler *ResourceHandler) Head(responseWriter http.ResponseWriter, request *http.Request) {
	redirectLocation, e := handler.blobstore.Head(mux.Vars(request)["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	responseWriter.WriteHeader(http.StatusOK)
}

func (handler *ResourceHandler) Get(responseWriter http.ResponseWriter, request *http.Request) {
	body, redirectLocation, e := handler.blobstore.Get(mux.Vars(request)["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	defer body.Close()
	responseWriter.WriteHeader(http.StatusOK)
	io.Copy(responseWriter, body)
}

func (handler *ResourceHandler) Delete(responseWriter http.ResponseWriter, request *http.Request) {
	exists, e := handler.blobstore.Exists(mux.Vars(request)["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	if !exists {
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	}

	e = handler.blobstore.Delete(mux.Vars(request)["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func (handler *ResourceHandler) DeleteDir(responseWriter http.ResponseWriter, request *http.Request) {
	e := handler.blobstore.DeletePrefix(mux.Vars(request)["identifier"])
	if e != nil {
		writeResponseBasedOnError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func writeResponseBasedOnError(responseWriter http.ResponseWriter, e error) {
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNoContent)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	responseWriter.WriteHeader(http.StatusNoContent)
}

func redirect(responseWriter http.ResponseWriter, redirectLocation string) {
	responseWriter.Header().Set("Location", redirectLocation)
	responseWriter.WriteHeader(http.StatusFound)
}

func internalServerError(responseWriter http.ResponseWriter, e error) {
	logger.Log.Error("Internal Server Error.", zap.String("error", fmt.Sprintf("%+v", e)))
	responseWriter.WriteHeader(http.StatusInternalServerError)
}

func badRequest(responseWriter http.ResponseWriter, message string, args ...interface{}) {
	responseWriter.WriteHeader(http.StatusBadRequest)
	fmt.Fprintf(responseWriter, message, args...)
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestSubMain_interrupt(t *testing.T) {
	t.Skip("This test isn't implemented for Windows yet")
}

/*************************************
 *  Helper functions
 *************************************/

func startService(t *testing.T, serviceName string) {
	runCmd(t, "net", "start", serviceName)
}

func stopService(t *testing.T, serviceName string) {
	runCmd(t, "net", "stop", serviceName)
}
<commit_msg>Fix windows service tests s/runCmd/runOSCmd/<commit_after>package main

import "testing"

func TestSubMain_interrupt(t *testing.T) {
	t.Skip("This test isn't implemented for Windows yet")
}

/*************************************
 *  Helper functions
 *************************************/

func startService(t *testing.T, serviceName string) {
	runOSCmd(t, "net", "start", serviceName)
}

func stopService(t *testing.T, serviceName string) {
	runOSCmd(t, "net", "stop", serviceName)
}
<|endoftext|>


<commit_before>package sphere

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var (
	address = func() string {
		l, _ := net.Listen("tcp", ":0")
		defer l.Close()
		return fmt.Sprintf("127.0.0.1:%d", l.Addr().(*net.TCPAddr).Port)
	}()
)

type TestSphereModel struct {
	*ChannelModel
}

func (m *TestSphereModel) Subscribe(room string, connection *Connection) bool {
	return true
}

func (m *TestSphereModel) Disconnect(room string, connection *Connection) bool {
	return true
}

func (m *TestSphereModel) Receive(event string, message string) (string, error) {
	return "you_got_me", nil
}

func init() {
	gin.SetMode(gin.ReleaseMode)
	a := NewRedisBroker()
	s, r := NewSphere(a), gin.New()
	s.ChannelModels(&TestSphereModel{ExtendChannelModel("test")})
	r.GET("/sync", func(c *gin.Context) {
		s.Handler(c.Writer, c.Request)
	})
	go r.Run(address)
}

func CreateConnection() (c *websocket.Conn, response *http.Response, err error) {
	u, err := url.Parse("ws://" + address + "/sync")
	if err != nil {
		return nil, nil, err
	}
	rawConn, err := net.Dial("tcp", u.Host)
	if err != nil {
		return nil, nil, err
	}
	wsHeaders := http.Header{
		"Origin": {"http://" + address},
	}
	return websocket.NewClient(rawConn, u, wsHeaders, 1024, 1024)
}

func TestSphereReady(t *testing.T) {
	count := 0
	ticker := time.NewTicker(time.Millisecond * 60)
	for {
		select {
		case <-ticker.C:
			s, err := net.Dial("tcp", address)
			defer s.Close()
			if err == nil || count > 3 {
				defer ticker.Stop()
				if err != nil {
					t.Fatal("Server could not get started")
				}
				return
			}
		}
		count++
	}
}

func TestSphereConnection(t *testing.T) {
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
}

func TestSphereSendMessage(t *testing.T) {
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	p := &Packet{Type: PacketTypePing}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
}

func TestSphereMessagePingPong(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			done <- nil
			return
		}
	}()
	p := &Packet{Type: PacketTypePing}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	<-done
}

func TestSphereSubscribeChannel(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			done <- nil
			return
		}
	}()
	p := &Packet{Type: PacketTypeSubscribe, Namespace: "test", Room: "helloworld"}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	if err := <-done; err != nil {
		t.Fatal(err.Error())
	}
}

func TestSphereChannelMessage(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, msg, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			p, err := ParsePacket(msg)
			if err != nil {
				done <- err
			} else {
				switch {
				case p.Type == PacketTypeSubscribed && p.Error == nil:
					m := &Packet{Type: PacketTypeChannel, Namespace: "test", Room: "helloworld", Message: &Message{Event: "HelloEvent", Data: "HelloWorld"}}
					if json, err := m.toJSON(); err == nil {
						if err := c.WriteMessage(TextMessage, json); err != nil {
							done <- err
						}
					} else {
						done <- err
					}
				case p.Type == PacketTypeChannel && p.Error == nil:
					done <- nil
				}
			}
		}
	}()
	p := &Packet{Type: PacketTypeSubscribe, Namespace: "test", Room: "helloworld"}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	if err := <-done; err != nil {
		t.Fatal(err.Error())
	}
}
<commit_msg>use SimpleBroker as default broker<commit_after>package sphere

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var (
	address = func() string {
		l, _ := net.Listen("tcp", ":0")
		defer l.Close()
		return fmt.Sprintf("127.0.0.1:%d", l.Addr().(*net.TCPAddr).Port)
	}()
)

type TestSphereModel struct {
	*ChannelModel
}

func (m *TestSphereModel) Subscribe(room string, connection *Connection) bool {
	return true
}

func (m *TestSphereModel) Disconnect(room string, connection *Connection) bool {
	return true
}

func (m *TestSphereModel) Receive(event string, message string) (string, error) {
	return "you_got_me", nil
}

func init() {
	gin.SetMode(gin.ReleaseMode)
	s, r := NewSphere(), gin.New()
	s.ChannelModels(&TestSphereModel{ExtendChannelModel("test")})
	r.GET("/sync", func(c *gin.Context) {
		s.Handler(c.Writer, c.Request)
	})
	go r.Run(address)
}

func CreateConnection() (c *websocket.Conn, response *http.Response, err error) {
	u, err := url.Parse("ws://" + address + "/sync")
	if err != nil {
		return nil, nil, err
	}
	rawConn, err := net.Dial("tcp", u.Host)
	if err != nil {
		return nil, nil, err
	}
	wsHeaders := http.Header{
		"Origin": {"http://" + address},
	}
	return websocket.NewClient(rawConn, u, wsHeaders, 1024, 1024)
}

func TestSphereReady(t *testing.T) {
	count := 0
	ticker := time.NewTicker(time.Millisecond * 60)
	for {
		select {
		case <-ticker.C:
			s, err := net.Dial("tcp", address)
			defer s.Close()
			if err == nil || count > 3 {
				defer ticker.Stop()
				if err != nil {
					t.Fatal("Server could not get started")
				}
				return
			}
		}
		count++
	}
}

func TestSphereConnection(t *testing.T) {
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
}

func TestSphereSendMessage(t *testing.T) {
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	p := &Packet{Type: PacketTypePing}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
}

func TestSphereMessagePingPong(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			done <- nil
			return
		}
	}()
	p := &Packet{Type: PacketTypePing}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	<-done
}

func TestSphereSubscribeChannel(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, _, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			done <- nil
			return
		}
	}()
	p := &Packet{Type: PacketTypeSubscribe, Namespace: "test", Room: "helloworld"}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	if err := <-done; err != nil {
		t.Fatal(err.Error())
	}
}

func TestSphereChannelMessage(t *testing.T) {
	done := make(chan error)
	c, _, err := CreateConnection()
	if err != nil {
		t.Fatal(err.Error())
	}
	defer c.Close()
	go func() {
		for {
			_, msg, err := c.ReadMessage()
			if err != nil {
				done <- err
				return
			}
			p, err := ParsePacket(msg)
			if err != nil {
				done <- err
			} else {
				switch {
				case p.Type == PacketTypeSubscribed && p.Error == nil:
					m := &Packet{Type: PacketTypeChannel, Namespace: "test", Room: "helloworld", Message: &Message{Event: "HelloEvent", Data: "HelloWorld"}}
					if json, err := m.toJSON(); err == nil {
						if err := c.WriteMessage(TextMessage, json); err != nil {
							done <- err
						}
					} else {
						done <- err
					}
				case p.Type == PacketTypeChannel && p.Error == nil:
					done <- nil
				}
			}
		}
	}()
	p := &Packet{Type: PacketTypeSubscribe, Namespace: "test", Room: "helloworld"}
	res, err := p.toJSON()
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := c.WriteMessage(TextMessage, res); err != nil {
		t.Fatal(err.Error())
	}
	if err := <-done; err != nil {
		t.Fatal(err.Error())
	}
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 v2

import (
	"context"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/containerd/containerd/namespaces"
	client "github.com/containerd/containerd/runtime/v2/shim"
	"github.com/pkg/errors"
)

type deferredPipeConnection struct {
	ctx context.Context

	wg   sync.WaitGroup
	once sync.Once

	c      net.Conn
	conerr error
}

func (dpc *deferredPipeConnection) Read(p []byte) (n int, err error) {
	if dpc.c == nil {
		dpc.wg.Wait()
		if dpc.c == nil {
			return 0, dpc.conerr
		}
	}
	return dpc.c.Read(p)
}
func (dpc *deferredPipeConnection) Close() error {
	var err error
	dpc.once.Do(func() {
		dpc.wg.Wait()
		if dpc.c != nil {
			err = dpc.c.Close()
		} else if dpc.conerr != nil {
			err = dpc.conerr
		}
	})
	return err
}

// openShimLog on Windows acts as the client of the log pipe. In this way the
// containerd daemon can reconnect to the shim log stream if it is restarted.
func openShimLog(ctx context.Context, bundle *Bundle) (io.ReadCloser, error) {
	ns, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	dpc := &deferredPipeConnection{
		ctx: ctx,
	}
	dpc.wg.Add(1)
	go func() {
		c, conerr := client.AnonDialer(
			fmt.Sprintf("\\\\.\\pipe\\containerd-shim-%s-%s-log", ns, bundle.ID),
			time.Second*10,
		)
		if conerr != nil {
			dpc.conerr = errors.Wrap(err, "failed to connect to shim log")
		}
		dpc.c = c
		dpc.wg.Done()
	}()
	return dpc, nil
}
<commit_msg>Fix a bug in shim log on Windows that can cause 100% CPU utilization<commit_after>/*
   Copyright The containerd 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 v2

import (
	"context"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/containerd/containerd/namespaces"
	client "github.com/containerd/containerd/runtime/v2/shim"
	"github.com/pkg/errors"
)

type deferredPipeConnection struct {
	ctx context.Context

	wg   sync.WaitGroup
	once sync.Once

	c      net.Conn
	conerr error
}

func (dpc *deferredPipeConnection) Read(p []byte) (n int, err error) {
	if dpc.c == nil {
		dpc.wg.Wait()
		if dpc.c == nil {
			return 0, dpc.conerr
		}
	}
	return dpc.c.Read(p)
}
func (dpc *deferredPipeConnection) Close() error {
	var err error
	dpc.once.Do(func() {
		dpc.wg.Wait()
		if dpc.c != nil {
			err = dpc.c.Close()
		} else if dpc.conerr != nil {
			err = dpc.conerr
		}
	})
	return err
}

// openShimLog on Windows acts as the client of the log pipe. In this way the
// containerd daemon can reconnect to the shim log stream if it is restarted.
func openShimLog(ctx context.Context, bundle *Bundle) (io.ReadCloser, error) {
	ns, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	dpc := &deferredPipeConnection{
		ctx: ctx,
	}
	dpc.wg.Add(1)
	go func() {
		c, conerr := client.AnonDialer(
			fmt.Sprintf("\\\\.\\pipe\\containerd-shim-%s-%s-log", ns, bundle.ID),
			time.Second*10,
		)
		if conerr != nil {
			dpc.conerr = errors.Wrap(conerr, "failed to connect to shim log")
		}
		dpc.c = c
		dpc.wg.Done()
	}()
	return dpc, nil
}
<|endoftext|>


<commit_before>package httplog

type HttpLogger interface {
	Fatal(...interface{})
	Fatalf(string, ...interface{})
	Fatalln(...interface{})

	Panic(...interface{})
	Panicf(string, ...interface{})
	Panicln(...interface{})

	Print(...interface{})
	Printf(string, ...interface{})
	Println(...interface{})

	BadRequest()
	Unauthorized()
	PaymentRequired()
	Forbidden()
	NotFound()
	MethodNotAllowed()
	NotAcceptable()
	ProxyAuthRequired()
	RequestTimeout()
	Conflict()
	Gone()
	LengthRequired()
	PreconditionFailed()
	RequestEntityTooLarge()
	RequestURITooLong()
	UnsupportedMediaType()
	RequestedRangeNotSatisfiable()
	ExpectationFailed()
	Teapot()

	InternalServerError()
	StatusNotImplemented()
	StatusBadGateway()
	ServiceUnavailable()
	GatewayTimeout()
	HTTPVersionNotSupported()
}
<commit_msg>updated HttpLogger interface. the methods for the 4xx client erros and 5xx server errors take an http.ResponseWriter as a parameter.<commit_after>package httplog

import (
	"net/http"
)

type HttpLogger interface {
	Fatal(...interface{})
	Fatalf(string, ...interface{})
	Fatalln(...interface{})

	Panic(...interface{})
	Panicf(string, ...interface{})
	Panicln(...interface{})

	Print(...interface{})
	Printf(string, ...interface{})
	Println(...interface{})

	BadRequest(http.ResponseWriter)
	Unauthorized(http.ResponseWriter)
	PaymentRequired(http.ResponseWriter)
	Forbidden(http.ResponseWriter)
	NotFound(http.ResponseWriter)
	MethodNotAllowed(http.ResponseWriter)
	NotAcceptable(http.ResponseWriter)
	ProxyAuthRequired(http.ResponseWriter)
	RequestTimeout(http.ResponseWriter)
	Conflict(http.ResponseWriter)
	Gone(http.ResponseWriter)
	LengthRequired(http.ResponseWriter)
	PreconditionFailed(http.ResponseWriter)
	RequestEntityTooLarge(http.ResponseWriter)
	RequestURITooLong(http.ResponseWriter)
	UnsupportedMediaType(http.ResponseWriter)
	RequestedRangeNotSatisfiable(http.ResponseWriter)
	ExpectationFailed(http.ResponseWriter)
	Teapot(http.ResponseWriter)

	InternalServerError(http.ResponseWriter)
	NotImplemented(http.ResponseWriter)
	BadGateway(http.ResponseWriter)
	ServiceUnavailable(http.ResponseWriter)
	GatewayTimeout(http.ResponseWriter)
	HTTPVersionNotSupported(http.ResponseWriter)
}
<|endoftext|>


<commit_before>package collectors

import (
	"github.com/StackExchange/tcollector/opentsdb"
	"github.com/StackExchange/wmi"
	"regexp"
)

func init() {
	collectors = append(collectors, c_cpu_windows)
	collectors = append(collectors, c_network_windows)
	collectors = append(collectors, c_physical_disk_windows)
	collectors = append(collectors, c_simple_mem_windows)
}

const CPU_QUERY = `
	SELECT Name, PercentPrivilegedTime, PercentInterruptTime, PercentUserTime
	FROM Win32_PerfRawData_PerfOS_Processor
	WHERE Name <> '_Total'
`

//KMB Moving Excludes to Go, we didn't notice the WHERE changing performance much and a regex
//is easier than building the WHERE string
const NETWORK_QUERY = `
	SELECT Name, BytesReceivedPerSec, BytesSentPerSec,
		PacketsReceivedPerSec, PacketsSentPerSec,
		PacketsOutboundDiscarded, PacketsOutboundErrors,
		PacketsReceivedDiscarded, PacketsReceivedErrors
	FROM Win32_PerfRawData_Tcpip_NetworkInterface
`
const INTERFACE_EXCLUSIONS = `isatap|Teredo`

const PHYSICAL_DISK_QUERY = `
	SELECT Name, AvgDisksecPerRead, AvgDisksecPerWrite,
		AvgDiskReadQueueLength, AvgDiskWriteQueueLength,
		DiskReadBytesPersec, DiskReadsPersec,
		DiskWriteBytesPersec, DiskWritesPersec,
		SplitIOPerSec, PercentDiskReadTime, PercentDiskWriteTime
	FROM Win32_PerfRawData_PerfDisk_PhysicalDisk
`

//Memory Needs to be expanded upon, Should be deeper in Utilization (What is Cache etc)
//as well as Saturation (i.e. Paging Activity). Lot of that is in Win32_PerfRawData_PerfOS_Memory

//Win32_Operating_System's units are KBytes
const SIMPLE_MEMORY_QUERY =  `
	SELECT FreePhysicalMemory, FreeVirtualMemory,
	TotalVisibleMemorySize, TotalVirtualMemorySize
	FROM Win32_OperatingSystem
`

func c_cpu_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_PerfOS_Processor
	err := wmi.Query(CPU_QUERY, &dst)
	if err != nil {
		l.Println("cpu:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "cpu.time", v.PercentPrivilegedTime, opentsdb.TagSet{"cpu": v.Name, "type": "privileged"})
		Add(&md, "cpu.time", v.PercentInterruptTime, opentsdb.TagSet{"cpu": v.Name, "type": "interrupt"})
		Add(&md, "cpu.time", v.PercentUserTime, opentsdb.TagSet{"cpu": v.Name, "type": "user"})
	}
	return md
}

func c_simple_mem_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_OperatingSystem
	err := wmi.Query(SIMPLE_MEMORY_QUERY, &dst)
	if err != nil {
		l.Println("simple_mem:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "mem.virtual.total", v.TotalVirtualMemorySize * 1024, opentsdb.TagSet{})
		Add(&md, "mem.virtual.free", v.FreeVirtualMemory * 1024, opentsdb.TagSet{})
		Add(&md, "mem.physical.total", v.TotalVisibleMemorySize * 1024, opentsdb.TagSet{})
		Add(&md, "mem.physical.free", v.FreePhysicalMemory * 1024, opentsdb.TagSet{})
	}
	return md
}

func c_physical_disk_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_PerfDisk_PhysicalDisk
	err := wmi.Query(PHYSICAL_DISK_QUERY, &dst)
	if err != nil {
		l.Println("disk_physical:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "disk.physical.duration", v.AvgDiskSecPerRead, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.duration", v.AvgDiskSecPerWrite, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.queue", v.AvgDiskReadQueueLength, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.queue", v.AvgDiskWriteQueueLength, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.ops", v.DiskReadsPerSec, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.ops", v.DiskWritesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.bytes", v.DiskReadBytesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.bytes", v.DiskWriteBytesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.percenttime", v.PercentDiskReadTime, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.percenttime", v.PercentDiskWriteTime, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.spltio", v.SplitIOPerSec, opentsdb.TagSet{"disk": v.Name})
	}
	return md
}

func c_network_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_Tcpip_NetworkInterface
	err := wmi.Query(NETWORK_QUERY, &dst)
	if err != nil {
		l.Println("network:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	exclusions := regexp.MustCompile(INTERFACE_EXCLUSIONS)
	for _, v := range dst {
		if ! exclusions.MatchString(v.Name) {
			Add(&md, "network.bytes", v.BytesReceivedPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "in"})
			Add(&md, "network.bytes", v.BytesSentPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "out"})
			Add(&md, "network.packets", v.PacketsReceivedPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "in"})
			Add(&md, "network.packets", v.PacketsSentPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "out"})
			Add(&md, "network.err", v.PacketsOutboundDiscarded, opentsdb.TagSet{"iface": v.Name, "type": "discard", "direction": "out"})
			Add(&md, "network.err", v.PacketsReceivedDiscarded, opentsdb.TagSet{"iface": v.Name, "type": "discard", "direction": "in"})
			Add(&md, "network.err", v.PacketsOutboundErrors, opentsdb.TagSet{"iface": v.Name, "type": "error", "direction": "out"})
			Add(&md, "network.err", v.PacketsReceivedErrors, opentsdb.TagSet{"iface": v.Name, "type": "error", "direction": "in"})
		}
	}
	return md
}

<commit_msg>cmd/scollector: Disk space utilization via wmi<commit_after>package collectors

import (
	"github.com/StackExchange/tcollector/opentsdb"
	"github.com/StackExchange/wmi"
	"regexp"
)

func init() {
	collectors = append(collectors, c_cpu_windows)
	collectors = append(collectors, c_network_windows)
	collectors = append(collectors, c_physical_disk_windows)
	collectors = append(collectors, c_simple_mem_windows)
	collectors = append(collectors, c_disk_space_windows)
}

const CPU_QUERY = `
	SELECT Name, PercentPrivilegedTime, PercentInterruptTime, PercentUserTime
	FROM Win32_PerfRawData_PerfOS_Processor
	WHERE Name <> '_Total'
`

//KMB Moving Excludes to Go, we didn't notice the WHERE changing performance much and a regex
//is easier than building the WHERE string
const NETWORK_QUERY = `
	SELECT Name, BytesReceivedPerSec, BytesSentPerSec,
		PacketsReceivedPerSec, PacketsSentPerSec,
		PacketsOutboundDiscarded, PacketsOutboundErrors,
		PacketsReceivedDiscarded, PacketsReceivedErrors
	FROM Win32_PerfRawData_Tcpip_NetworkInterface
`
const INTERFACE_EXCLUSIONS = `isatap|Teredo`

const PHYSICAL_DISK_QUERY = `
	SELECT Name, AvgDisksecPerRead, AvgDisksecPerWrite,
		AvgDiskReadQueueLength, AvgDiskWriteQueueLength,
		DiskReadBytesPersec, DiskReadsPersec,
		DiskWriteBytesPersec, DiskWritesPersec,
		SplitIOPerSec, PercentDiskReadTime, PercentDiskWriteTime
	FROM Win32_PerfRawData_PerfDisk_PhysicalDisk
	WHERE Name <> '_Total'
`

//Similar Breakdowns exist as to physical, but for now just using this for the space utilization
const DISKSPACE_QUERY = `
	SELECT Name, FreeMegaBytes, PercentFreeSpace
	FROM Win32_PerfRawData_PerfDisk_LogicalDisk
	WHERE Name <> '_Total'
`

//Memory Needs to be expanded upon, Should be deeper in Utilization (What is Cache etc)
//as well as Saturation (i.e. Paging Activity). Lot of that is in Win32_PerfRawData_PerfOS_Memory
//Win32_Operating_System's units are KBytes

const SIMPLE_MEMORY_QUERY =  `
	SELECT FreePhysicalMemory, FreeVirtualMemory,
	TotalVisibleMemorySize, TotalVirtualMemorySize
	FROM Win32_OperatingSystem
`

func c_cpu_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_PerfOS_Processor
	err := wmi.Query(CPU_QUERY, &dst)
	if err != nil {
		l.Println("cpu:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "cpu.time", v.PercentPrivilegedTime, opentsdb.TagSet{"cpu": v.Name, "type": "privileged"})
		Add(&md, "cpu.time", v.PercentInterruptTime, opentsdb.TagSet{"cpu": v.Name, "type": "interrupt"})
		Add(&md, "cpu.time", v.PercentUserTime, opentsdb.TagSet{"cpu": v.Name, "type": "user"})
	}
	return md
}

func c_simple_mem_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_OperatingSystem
	err := wmi.Query(SIMPLE_MEMORY_QUERY, &dst)
	if err != nil {
		l.Println("simple_mem:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "mem.virtual.total", v.TotalVirtualMemorySize * 1024, opentsdb.TagSet{})
		Add(&md, "mem.virtual.free", v.FreeVirtualMemory * 1024, opentsdb.TagSet{})
		Add(&md, "mem.physical.total", v.TotalVisibleMemorySize * 1024, opentsdb.TagSet{})
		Add(&md, "mem.physical.free", v.FreePhysicalMemory * 1024, opentsdb.TagSet{})
	}
	return md
}

func c_disk_space_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_PerfDisk_LogicalDisk
	err := wmi.Query(DISKSPACE_QUERY, &dst)
	if err != nil {
		l.Println("simple_mem:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "disk.logical.free_bytes", v.FreeMegabytes * 1048576, opentsdb.TagSet{"partition": v.Name})
		Add(&md, "disk.logical.percent_free", v.PercentFreeSpace, opentsdb.TagSet{"partition": v.Name})
	}
	return md
}

func c_physical_disk_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_PerfDisk_PhysicalDisk
	err := wmi.Query(PHYSICAL_DISK_QUERY, &dst)
	if err != nil {
		l.Println("disk_physical:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		Add(&md, "disk.physical.duration", v.AvgDiskSecPerRead, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.duration", v.AvgDiskSecPerWrite, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.queue", v.AvgDiskReadQueueLength, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.queue", v.AvgDiskWriteQueueLength, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.ops", v.DiskReadsPerSec, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.ops", v.DiskWritesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.bytes", v.DiskReadBytesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.bytes", v.DiskWriteBytesPerSec, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.percenttime", v.PercentDiskReadTime, opentsdb.TagSet{"disk": v.Name, "type": "read"})
		Add(&md, "disk.physical.percenttime", v.PercentDiskWriteTime, opentsdb.TagSet{"disk": v.Name, "type": "write"})
		Add(&md, "disk.physical.spltio", v.SplitIOPerSec, opentsdb.TagSet{"disk": v.Name})
	}
	return md
}

func c_network_windows() opentsdb.MultiDataPoint {
	var dst []wmi.Win32_PerfRawData_Tcpip_NetworkInterface
	err := wmi.Query(NETWORK_QUERY, &dst)
	if err != nil {
		l.Println("network:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	exclusions := regexp.MustCompile(INTERFACE_EXCLUSIONS)
	for _, v := range dst {
		if ! exclusions.MatchString(v.Name) {
			Add(&md, "network.bytes", v.BytesReceivedPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "in"})
			Add(&md, "network.bytes", v.BytesSentPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "out"})
			Add(&md, "network.packets", v.PacketsReceivedPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "in"})
			Add(&md, "network.packets", v.PacketsSentPerSec, opentsdb.TagSet{"iface": v.Name, "direction": "out"})
			Add(&md, "network.err", v.PacketsOutboundDiscarded, opentsdb.TagSet{"iface": v.Name, "type": "discard", "direction": "out"})
			Add(&md, "network.err", v.PacketsReceivedDiscarded, opentsdb.TagSet{"iface": v.Name, "type": "discard", "direction": "in"})
			Add(&md, "network.err", v.PacketsOutboundErrors, opentsdb.TagSet{"iface": v.Name, "type": "error", "direction": "out"})
			Add(&md, "network.err", v.PacketsReceivedErrors, opentsdb.TagSet{"iface": v.Name, "type": "error", "direction": "in"})
		}
	}
	return md
}

<|endoftext|>


<commit_before>// Same copyright and license as the rest of the files in this project
// This file contains style related functions and structures

package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/pango"

	"github.com/gotk3/gotk3/glib"
)

/*
 * GtkLabel
 */

// Label is a representation of GTK's GtkLabel.
type Label struct {
	Widget
}

// native returns a pointer to the underlying GtkLabel.
func (v *Label) native() *C.GtkLabel {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkLabel(p)
}

func marshalLabel(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

func wrapLabel(obj *glib.Object) *Label {
	return &Label{Widget{glib.InitiallyUnowned{obj}}}
}

func WidgetToLabel(widget *Widget) (interface{}, error) {
	obj := glib.Take(unsafe.Pointer(widget.GObject))
	return wrapLabel(obj), nil
}

// LabelNew is a wrapper around gtk_label_new().
func LabelNew(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SetText is a wrapper around gtk_label_set_text().
func (v *Label) SetText(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_text(v.native(), (*C.gchar)(cstr))
}

// TODO:
// gtk_label_set_text_with_mnemonic().
// gtk_label_set_attributes().
// gtk_label_get_attributes().

// SetMarkup is a wrapper around gtk_label_set_markup().
func (v *Label) SetMarkup(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup(v.native(), (*C.gchar)(cstr))
}

// SetMarkupWithMnemonic is a wrapper around
// gtk_label_set_markup_with_mnemonic().
func (v *Label) SetMarkupWithMnemonic(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup_with_mnemonic(v.native(), (*C.gchar)(cstr))
}

// SetPattern is a wrapper around gtk_label_set_pattern().
func (v *Label) SetPattern(patern string) {
	cstr := C.CString(patern)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_pattern(v.native(), (*C.gchar)(cstr))
}

// SetJustify is a wrapper around gtk_label_set_justify().
func (v *Label) SetJustify(jtype Justification) {
	C.gtk_label_set_justify(v.native(), C.GtkJustification(jtype))
}

// SetEllipsize is a wrapper around gtk_label_set_ellipsize().
func (v *Label) SetEllipsize(mode pango.EllipsizeMode) {
	C.gtk_label_set_ellipsize(v.native(), C.PangoEllipsizeMode(mode))
}

// GetWidthChars is a wrapper around gtk_label_get_width_chars().
func (v *Label) GetWidthChars() int {
	c := C.gtk_label_get_width_chars(v.native())
	return int(c)
}

// SetWidthChars is a wrapper around gtk_label_set_width_chars().
func (v *Label) SetWidthChars(nChars int) {
	C.gtk_label_set_width_chars(v.native(), C.gint(nChars))
}

// GetMaxWidthChars is a wrapper around gtk_label_get_max_width_chars().
func (v *Label) GetMaxWidthChars() int {
	c := C.gtk_label_get_max_width_chars(v.native())
	return int(c)
}

// SetMaxWidthChars is a wrapper around gtk_label_set_max_width_chars().
func (v *Label) SetMaxWidthChars(nChars int) {
	C.gtk_label_set_max_width_chars(v.native(), C.gint(nChars))
}

// GetLineWrap is a wrapper around gtk_label_get_line_wrap().
func (v *Label) GetLineWrap() bool {
	c := C.gtk_label_get_line_wrap(v.native())
	return gobool(c)
}

// SetLineWrap is a wrapper around gtk_label_set_line_wrap().
func (v *Label) SetLineWrap(wrap bool) {
	C.gtk_label_set_line_wrap(v.native(), gbool(wrap))
}

// SetLineWrapMode is a wrapper around gtk_label_set_line_wrap_mode().
func (v *Label) SetLineWrapMode(wrapMode pango.WrapMode) {
	C.gtk_label_set_line_wrap_mode(v.native(), C.PangoWrapMode(wrapMode))
}

// TODO:
// gtk_label_get_line_wrap_mode().
// gtk_label_get_layout_offsets().
// gtk_label_get_layout().
// gtk_label_get_mnemonic_widget().

// GetSelectable is a wrapper around gtk_label_get_selectable().
func (v *Label) GetSelectable() bool {
	c := C.gtk_label_get_selectable(v.native())
	return gobool(c)
}

// GetText is a wrapper around gtk_label_get_text().
func (v *Label) GetText() (string, error) {
	c := C.gtk_label_get_text(v.native())
	if c == nil {
		return "", nilPtrErr
	}
	return C.GoString((*C.char)(c)), nil
}

// GetJustify is a wrapper around gtk_label_get_justify().
func (v *Label) GetJustify() Justification {
	c := C.gtk_label_get_justify(v.native())
	return Justification(c)
}

// GetEllipsize is a wrapper around gtk_label_get_ellipsize().
func (v *Label) GetEllipsize() pango.EllipsizeMode {
	c := C.gtk_label_get_ellipsize(v.native())
	return pango.EllipsizeMode(c)
}

// GetCurrentUri is a wrapper around gtk_label_get_current_uri().
func (v *Label) GetCurrentUri() string {
	c := C.gtk_label_get_current_uri(v.native())
	return C.GoString((*C.char)(c))
}

// GetTrackVisitedLinks is a wrapper around gtk_label_get_track_visited_links().
func (v *Label) GetTrackVisitedLinks() bool {
	c := C.gtk_label_get_track_visited_links(v.native())
	return gobool(c)
}

// SetTrackVisitedLinks is a wrapper around gtk_label_set_track_visited_links().
func (v *Label) SetTrackVisitedLinks(trackLinks bool) {
	C.gtk_label_set_track_visited_links(v.native(), gbool(trackLinks))
}

// GetAngle is a wrapper around gtk_label_get_angle().
func (v *Label) GetAngle() float64 {
	c := C.gtk_label_get_angle(v.native())
	return float64(c)
}

// SetAngle is a wrapper around gtk_label_set_angle().
func (v *Label) SetAngle(angle float64) {
	C.gtk_label_set_angle(v.native(), C.gdouble(angle))
}

// GetSelectionBounds is a wrapper around gtk_label_get_selection_bounds().
func (v *Label) GetSelectionBounds() (start, end int, nonEmpty bool) {
	var cstart, cend C.gint
	c := C.gtk_label_get_selection_bounds(v.native(), &cstart, &cend)
	return int(cstart), int(cend), gobool(c)
}

// GetSingleLineMode is a wrapper around gtk_label_get_single_line_mode().
func (v *Label) GetSingleLineMode() bool {
	c := C.gtk_label_get_single_line_mode(v.native())
	return gobool(c)
}

// SetSingleLineMode is a wrapper around gtk_label_set_single_line_mode().
func (v *Label) SetSingleLineMode(mode bool) {
	C.gtk_label_set_single_line_mode(v.native(), gbool(mode))
}

// GetUseMarkup is a wrapper around gtk_label_get_use_markup().
func (v *Label) GetUseMarkup() bool {
	c := C.gtk_label_get_use_markup(v.native())
	return gobool(c)
}

// SetUseMarkup is a wrapper around gtk_label_set_use_markup().
func (v *Label) SetUseMarkup(use bool) {
	C.gtk_label_set_use_markup(v.native(), gbool(use))
}

// GetUseUnderline is a wrapper around gtk_label_get_use_underline().
func (v *Label) GetUseUnderline() bool {
	c := C.gtk_label_get_use_underline(v.native())
	return gobool(c)
}

// SetUseUnderline is a wrapper around gtk_label_set_use_underline().
func (v *Label) SetUseUnderline(use bool) {
	C.gtk_label_set_use_underline(v.native(), gbool(use))
}

// LabelNewWithMnemonic is a wrapper around gtk_label_new_with_mnemonic().
func LabelNewWithMnemonic(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new_with_mnemonic((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SelectRegion is a wrapper around gtk_label_select_region().
func (v *Label) SelectRegion(startOffset, endOffset int) {
	C.gtk_label_select_region(v.native(), C.gint(startOffset),
		C.gint(endOffset))
}

// SetSelectable is a wrapper around gtk_label_set_selectable().
func (v *Label) SetSelectable(setting bool) {
	C.gtk_label_set_selectable(v.native(), gbool(setting))
}

// SetLabel is a wrapper around gtk_label_set_label().
func (v *Label) SetLabel(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_label(v.native(), (*C.gchar)(cstr))
}

// GetLabel is a wrapper around gtk_label_get_label().
func (v *Label) GetLabel() string {
	c := C.gtk_label_get_label(v.native())
	if c == nil {
		return ""
	}
	return C.GoString((*C.char)(c))
}

// GetMnemonicKeyval is a wrapper around gtk_label_get_mnemonic_keyval().
func (v *Label) GetMnemonicKeyval() uint {
	return uint(C.gtk_label_get_mnemonic_keyval(v.native()))
}

// SetMnemonicWidget is a wrapper around gtk_label_set_mnemonic_widget().
func (v *Label) SetMnemonicWidget(widget IWidget) {
	C.gtk_label_set_mnemonic_widget(v.native(), widget.toWidget())
}
<commit_msg>if you say you cast to *Label, then return that<commit_after>// Same copyright and license as the rest of the files in this project
// This file contains style related functions and structures

package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/pango"

	"github.com/gotk3/gotk3/glib"
)

/*
 * GtkLabel
 */

// Label is a representation of GTK's GtkLabel.
type Label struct {
	Widget
}

// native returns a pointer to the underlying GtkLabel.
func (v *Label) native() *C.GtkLabel {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkLabel(p)
}

func marshalLabel(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

func wrapLabel(obj *glib.Object) *Label {
	return &Label{Widget{glib.InitiallyUnowned{obj}}}
}

// WidgetToLabel is a convience func that casts the given *Widget into a *Label.
func WidgetToLabel(widget *Widget) (*Label, error) {
	obj := glib.Take(unsafe.Pointer(widget.GObject))
	return wrapLabel(obj), nil
}

// LabelNew is a wrapper around gtk_label_new().
func LabelNew(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SetText is a wrapper around gtk_label_set_text().
func (v *Label) SetText(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_text(v.native(), (*C.gchar)(cstr))
}

// TODO:
// gtk_label_set_text_with_mnemonic().
// gtk_label_set_attributes().
// gtk_label_get_attributes().

// SetMarkup is a wrapper around gtk_label_set_markup().
func (v *Label) SetMarkup(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup(v.native(), (*C.gchar)(cstr))
}

// SetMarkupWithMnemonic is a wrapper around
// gtk_label_set_markup_with_mnemonic().
func (v *Label) SetMarkupWithMnemonic(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup_with_mnemonic(v.native(), (*C.gchar)(cstr))
}

// SetPattern is a wrapper around gtk_label_set_pattern().
func (v *Label) SetPattern(patern string) {
	cstr := C.CString(patern)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_pattern(v.native(), (*C.gchar)(cstr))
}

// SetJustify is a wrapper around gtk_label_set_justify().
func (v *Label) SetJustify(jtype Justification) {
	C.gtk_label_set_justify(v.native(), C.GtkJustification(jtype))
}

// SetEllipsize is a wrapper around gtk_label_set_ellipsize().
func (v *Label) SetEllipsize(mode pango.EllipsizeMode) {
	C.gtk_label_set_ellipsize(v.native(), C.PangoEllipsizeMode(mode))
}

// GetWidthChars is a wrapper around gtk_label_get_width_chars().
func (v *Label) GetWidthChars() int {
	c := C.gtk_label_get_width_chars(v.native())
	return int(c)
}

// SetWidthChars is a wrapper around gtk_label_set_width_chars().
func (v *Label) SetWidthChars(nChars int) {
	C.gtk_label_set_width_chars(v.native(), C.gint(nChars))
}

// GetMaxWidthChars is a wrapper around gtk_label_get_max_width_chars().
func (v *Label) GetMaxWidthChars() int {
	c := C.gtk_label_get_max_width_chars(v.native())
	return int(c)
}

// SetMaxWidthChars is a wrapper around gtk_label_set_max_width_chars().
func (v *Label) SetMaxWidthChars(nChars int) {
	C.gtk_label_set_max_width_chars(v.native(), C.gint(nChars))
}

// GetLineWrap is a wrapper around gtk_label_get_line_wrap().
func (v *Label) GetLineWrap() bool {
	c := C.gtk_label_get_line_wrap(v.native())
	return gobool(c)
}

// SetLineWrap is a wrapper around gtk_label_set_line_wrap().
func (v *Label) SetLineWrap(wrap bool) {
	C.gtk_label_set_line_wrap(v.native(), gbool(wrap))
}

// SetLineWrapMode is a wrapper around gtk_label_set_line_wrap_mode().
func (v *Label) SetLineWrapMode(wrapMode pango.WrapMode) {
	C.gtk_label_set_line_wrap_mode(v.native(), C.PangoWrapMode(wrapMode))
}

// TODO:
// gtk_label_get_line_wrap_mode().
// gtk_label_get_layout_offsets().
// gtk_label_get_layout().
// gtk_label_get_mnemonic_widget().

// GetSelectable is a wrapper around gtk_label_get_selectable().
func (v *Label) GetSelectable() bool {
	c := C.gtk_label_get_selectable(v.native())
	return gobool(c)
}

// GetText is a wrapper around gtk_label_get_text().
func (v *Label) GetText() (string, error) {
	c := C.gtk_label_get_text(v.native())
	if c == nil {
		return "", nilPtrErr
	}
	return C.GoString((*C.char)(c)), nil
}

// GetJustify is a wrapper around gtk_label_get_justify().
func (v *Label) GetJustify() Justification {
	c := C.gtk_label_get_justify(v.native())
	return Justification(c)
}

// GetEllipsize is a wrapper around gtk_label_get_ellipsize().
func (v *Label) GetEllipsize() pango.EllipsizeMode {
	c := C.gtk_label_get_ellipsize(v.native())
	return pango.EllipsizeMode(c)
}

// GetCurrentUri is a wrapper around gtk_label_get_current_uri().
func (v *Label) GetCurrentUri() string {
	c := C.gtk_label_get_current_uri(v.native())
	return C.GoString((*C.char)(c))
}

// GetTrackVisitedLinks is a wrapper around gtk_label_get_track_visited_links().
func (v *Label) GetTrackVisitedLinks() bool {
	c := C.gtk_label_get_track_visited_links(v.native())
	return gobool(c)
}

// SetTrackVisitedLinks is a wrapper around gtk_label_set_track_visited_links().
func (v *Label) SetTrackVisitedLinks(trackLinks bool) {
	C.gtk_label_set_track_visited_links(v.native(), gbool(trackLinks))
}

// GetAngle is a wrapper around gtk_label_get_angle().
func (v *Label) GetAngle() float64 {
	c := C.gtk_label_get_angle(v.native())
	return float64(c)
}

// SetAngle is a wrapper around gtk_label_set_angle().
func (v *Label) SetAngle(angle float64) {
	C.gtk_label_set_angle(v.native(), C.gdouble(angle))
}

// GetSelectionBounds is a wrapper around gtk_label_get_selection_bounds().
func (v *Label) GetSelectionBounds() (start, end int, nonEmpty bool) {
	var cstart, cend C.gint
	c := C.gtk_label_get_selection_bounds(v.native(), &cstart, &cend)
	return int(cstart), int(cend), gobool(c)
}

// GetSingleLineMode is a wrapper around gtk_label_get_single_line_mode().
func (v *Label) GetSingleLineMode() bool {
	c := C.gtk_label_get_single_line_mode(v.native())
	return gobool(c)
}

// SetSingleLineMode is a wrapper around gtk_label_set_single_line_mode().
func (v *Label) SetSingleLineMode(mode bool) {
	C.gtk_label_set_single_line_mode(v.native(), gbool(mode))
}

// GetUseMarkup is a wrapper around gtk_label_get_use_markup().
func (v *Label) GetUseMarkup() bool {
	c := C.gtk_label_get_use_markup(v.native())
	return gobool(c)
}

// SetUseMarkup is a wrapper around gtk_label_set_use_markup().
func (v *Label) SetUseMarkup(use bool) {
	C.gtk_label_set_use_markup(v.native(), gbool(use))
}

// GetUseUnderline is a wrapper around gtk_label_get_use_underline().
func (v *Label) GetUseUnderline() bool {
	c := C.gtk_label_get_use_underline(v.native())
	return gobool(c)
}

// SetUseUnderline is a wrapper around gtk_label_set_use_underline().
func (v *Label) SetUseUnderline(use bool) {
	C.gtk_label_set_use_underline(v.native(), gbool(use))
}

// LabelNewWithMnemonic is a wrapper around gtk_label_new_with_mnemonic().
func LabelNewWithMnemonic(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new_with_mnemonic((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SelectRegion is a wrapper around gtk_label_select_region().
func (v *Label) SelectRegion(startOffset, endOffset int) {
	C.gtk_label_select_region(v.native(), C.gint(startOffset),
		C.gint(endOffset))
}

// SetSelectable is a wrapper around gtk_label_set_selectable().
func (v *Label) SetSelectable(setting bool) {
	C.gtk_label_set_selectable(v.native(), gbool(setting))
}

// SetLabel is a wrapper around gtk_label_set_label().
func (v *Label) SetLabel(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_label(v.native(), (*C.gchar)(cstr))
}

// GetLabel is a wrapper around gtk_label_get_label().
func (v *Label) GetLabel() string {
	c := C.gtk_label_get_label(v.native())
	if c == nil {
		return ""
	}
	return C.GoString((*C.char)(c))
}

// GetMnemonicKeyval is a wrapper around gtk_label_get_mnemonic_keyval().
func (v *Label) GetMnemonicKeyval() uint {
	return uint(C.gtk_label_get_mnemonic_keyval(v.native()))
}

// SetMnemonicWidget is a wrapper around gtk_label_set_mnemonic_widget().
func (v *Label) SetMnemonicWidget(widget IWidget) {
	C.gtk_label_set_mnemonic_widget(v.native(), widget.toWidget())
}
<|endoftext|>