<commit_before>package charond

import (
	"database/sql"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes"
	pbts "github.com/golang/protobuf/ptypes/timestamp"
	"github.com/piotrkowalczuk/charon"
	"github.com/piotrkowalczuk/ntypes"
)

const (
	// UserConfirmationTokenUsed is a value that is used when confirmation token was already used.
	UserConfirmationTokenUsed = "!"
)

// String return concatenated first and last name of the user.
// Implements fmt Stringer interface.
func (ue *userEntity) String() string {
	switch {
	case ue.FirstName == "":
		return ue.LastName
	case ue.LastName == "":
		return ue.FirstName
	default:
		return ue.FirstName + " " + ue.LastName
	}
}

func (ue *userEntity) message() (*charon.User, error) {
	var (
		err                  error
		createdAt, updatedAt *pbts.Timestamp
	)

	if createdAt, err = ptypes.TimestampProto(ue.CreatedAt); err != nil {
		return nil, err
	}
	if ue.UpdatedAt != nil {
		if createdAt, err = ptypes.TimestampProto(*ue.UpdatedAt); err != nil {
			return nil, err
		}
	}

	return &charon.User{
		Id:          ue.ID,
		Username:    ue.Username,
		FirstName:   ue.FirstName,
		LastName:    ue.LastName,
		IsSuperuser: ue.IsSuperuser,
		IsActive:    ue.IsActive,
		IsStaff:     ue.IsStaff,
		IsConfirmed: ue.IsConfirmed,
		CreatedAt:   createdAt,
		CreatedBy:   ue.CreatedBy,
		UpdatedAt:   updatedAt,
		UpdatedBy:   ue.UpdatedBy,
	}, nil
}

type userProvider interface {
	Exists(id int64) (bool, error)
	Create(username string, password []byte, firstName, lastName string, confirmationToken []byte, isSuperuser, isStaff, isActive, isConfirmed bool) (*userEntity, error)
	Insert(*userEntity) (*userEntity, error)
	CreateSuperuser(username string, password []byte, firstName, lastName string) (*userEntity, error)
	// Count retrieves number of all users.
	Count() (int64, error)
	UpdateLastLoginAt(id int64) (int64, error)
	ChangePassword(id int64, password string) error
	Find(criteria *userCriteria) ([]*userEntity, error)
	FindOneByID(id int64) (*userEntity, error)
	FindOneByUsername(username string) (*userEntity, error)
	DeleteByID(id int64) (int64, error)
	UpdateByID(
		id int64,
		confirmationToken []byte,
		createdAt *time.Time,
		createdBy *ntypes.Int64,
		firstName *ntypes.String,
		isActive *ntypes.Bool,
		isConfirmed *ntypes.Bool,
		isStaff *ntypes.Bool,
		isSuperuser *ntypes.Bool,
		lastLoginAt *time.Time,
		lastName *ntypes.String,
		password []byte,
		updatedAt *time.Time,
		updatedBy *ntypes.Int64,
		username *ntypes.String,
	) (*userEntity, error)
	RegistrationConfirmation(id int64, confirmationToken string) error
	IsGranted(id int64, permission charon.Permission) (bool, error)
	SetPermissions(id int64, permissions ...charon.Permission) (int64, int64, error)
}

func newuserProvider(dbPool *sql.DB) userProvider {
	return &userRepository{
		db:      dbPool,
		table:   tableUser,
		columns: tableUserColumns,
	}
}

// Create implements userProvider interface.
func (ur *userRepository) Create(username string, password []byte, firstName, lastName string, confirmationToken []byte, isSuperuser, isStaff, isActive, isConfirmed bool) (*userEntity, error) {
	if isSuperuser {
		isStaff = true
		isActive = true
		isConfirmed = true
	}

	entity := &userEntity{
		Username:          username,
		Password:          password,
		FirstName:         firstName,
		LastName:          lastName,
		ConfirmationToken: confirmationToken,
		IsSuperuser:       isSuperuser,
		IsStaff:           isStaff,
		IsActive:          isActive,
		IsConfirmed:       isConfirmed,
	}
	return ur.Insert(entity)
}

// CreateSuperuser implements userProvider interface.
func (ur *userRepository) CreateSuperuser(username string, password []byte, firstName, lastName string) (*userEntity, error) {
	return ur.Create(username, password, firstName, lastName, []byte(UserConfirmationTokenUsed), true, false, true, true)
}

// Count implements userProvider interface.
func (ur *userRepository) Count() (n int64, err error) {
	err = ur.db.QueryRow("SELECT COUNT(*) FROM " + tableUser).Scan(&n)

	return
}

func (ur *userRepository) RegistrationConfirmation(userID int64, confirmationToken string) error {
	query := `
		UPDATE ` + ur.table + `
		SET is_confirmed = true, is_active = true, updated_at = NOW(), confirmation_token = $1
		WHERE is_confirmed = false AND id = $2 AND confirmation_token = $3;
	`

	result, err := ur.db.Exec(
		query,
		UserConfirmationTokenUsed,
		userID,
		confirmationToken,
	)
	if err != nil {
		return err
	}

	_, err = result.RowsAffected()

	return err
}

// ChangePassword ...
func (ur *userRepository) ChangePassword(userID int64, password string) error {
	query := `
		UPDATE ` + ur.table + `
		SET password = $2, updated_at = NOW()
		WHERE id = $1;
	`

	result, err := ur.db.Exec(
		query,
		userID,
		password,
	)
	if err != nil {
		return err
	}
	_, err = result.RowsAffected()

	return err
}

// FindOneByUsername ...
func (ur *userRepository) FindOneByUsername(username string) (*userEntity, error) {
	return ur.findOneBy("username", username)
}

func (ur *userRepository) findOneBy(fieldName string, value interface{}) (*userEntity, error) {
	query := `
		SELECT ` + strings.Join(tableUserColumns, ",") + `
		FROM ` + ur.table + `
		WHERE ` + fieldName + ` = $1
		LIMIT 1
	`

	var user userEntity
	err := ur.db.QueryRow(query, value).Scan(
		&user.ConfirmationToken,
		&user.CreatedAt,
		&user.CreatedBy,
		&user.FirstName,
		&user.ID,
		&user.IsActive,
		&user.IsConfirmed,
		&user.IsStaff,
		&user.IsSuperuser,
		&user.LastLoginAt,
		&user.LastName,
		&user.Password,
		&user.UpdatedAt,
		&user.UpdatedBy,
		&user.Username,
	)

	if err != nil {
		return nil, err
	}

	return &user, nil
}

// UpdateLastLoginAt implements userProvider interface.
func (ur *userRepository) UpdateLastLoginAt(userID int64) (int64, error) {
	query := `
		UPDATE ` + ur.table + `
		SET ` + tableUserColumnLastLoginAt + ` = NOW()
		WHERE ` + tableUserColumnID + ` = $1
	`

	result, err := ur.db.Exec(
		query,
		userID,
	)
	if err != nil {
		return 0, err
	}

	return result.RowsAffected()
}

// Exists implements userProvider interface.
func (ur *userRepository) Exists(userID int64) (bool, error) {
	query := `
		SELECT EXISTS(
			SELECT 1
			FROM ` + ur.table + ` AS p
			WHERE ` + tableUserColumnID + ` = $1
		)
	`

	var exists bool
	if err := ur.db.QueryRow(query, userID).Scan(&exists); err != nil {
		return false, err
	}

	return exists, nil
}

// IsGranted implements userProvider interface.
func (ur *userRepository) IsGranted(id int64, p charon.Permission) (bool, error) {
	var exists bool
	subsystem, module, action := p.Split()
	if err := ur.db.QueryRow(isGrantedQuery(
		tableUserPermissions,
		tableUserPermissionsColumnUserID,
		tableUserPermissionsColumnPermissionSubsystem,
		tableUserPermissionsColumnPermissionModule,
		tableUserPermissionsColumnPermissionAction,
	), id, subsystem, module, action).Scan(&exists); err != nil {
		return false, err
	}

	return exists, nil
}

// SetPermissions implements userProvider interface.
func (ur *userRepository) SetPermissions(id int64, p ...charon.Permission) (int64, int64, error) {
	return setPermissions(ur.db, tableUserPermissions,
		tableUserPermissionsColumnUserID,
		tableUserPermissionsColumnPermissionSubsystem,
		tableUserPermissionsColumnPermissionModule,
		tableUserPermissionsColumnPermissionAction, id, p)
}

func createDummyTestUser(repo userProvider, hasher charon.PasswordHasher) (*userEntity, error) {
	password, err := hasher.Hash([]byte("test"))
	if err != nil {
		return nil, err
	}
	return repo.CreateSuperuser("test", password, "Test", "Test")
}
<commit_msg>function signature fix<commit_after>package charond

import (
	"database/sql"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes"
	pbts "github.com/golang/protobuf/ptypes/timestamp"
	"github.com/piotrkowalczuk/charon"
	"github.com/piotrkowalczuk/ntypes"
)

const (
	// UserConfirmationTokenUsed is a value that is used when confirmation token was already used.
	UserConfirmationTokenUsed = "!"
)

// String return concatenated first and last name of the user.
// Implements fmt Stringer interface.
func (ue *userEntity) String() string {
	switch {
	case ue.FirstName == "":
		return ue.LastName
	case ue.LastName == "":
		return ue.FirstName
	default:
		return ue.FirstName + " " + ue.LastName
	}
}

func (ue *userEntity) message() (*charon.User, error) {
	var (
		err                  error
		createdAt, updatedAt *pbts.Timestamp
	)

	if createdAt, err = ptypes.TimestampProto(ue.CreatedAt); err != nil {
		return nil, err
	}
	if ue.UpdatedAt != nil {
		if createdAt, err = ptypes.TimestampProto(*ue.UpdatedAt); err != nil {
			return nil, err
		}
	}

	return &charon.User{
		Id:          ue.ID,
		Username:    ue.Username,
		FirstName:   ue.FirstName,
		LastName:    ue.LastName,
		IsSuperuser: ue.IsSuperuser,
		IsActive:    ue.IsActive,
		IsStaff:     ue.IsStaff,
		IsConfirmed: ue.IsConfirmed,
		CreatedAt:   createdAt,
		CreatedBy:   ue.CreatedBy,
		UpdatedAt:   updatedAt,
		UpdatedBy:   ue.UpdatedBy,
	}, nil
}

type userProvider interface {
	Exists(id int64) (bool, error)
	Create(username string, password []byte, firstName, lastName string, confirmationToken []byte, isSuperuser, isStaff, isActive, isConfirmed bool) (*userEntity, error)
	Insert(*userEntity) (*userEntity, error)
	CreateSuperuser(username string, password []byte, firstName, lastName string) (*userEntity, error)
	// Count retrieves number of all users.
	Count() (int64, error)
	UpdateLastLoginAt(id int64) (int64, error)
	ChangePassword(id int64, password string) error
	Find(criteria *userCriteria) ([]*userEntity, error)
	FindOneByID(id int64) (*userEntity, error)
	FindOneByUsername(username string) (*userEntity, error)
	DeleteByID(id int64) (int64, error)
	UpdateByID(
		id int64,
		confirmationToken []byte,
		createdAt *time.Time,
		createdBy *ntypes.Int64,
		firstName *ntypes.String,
		isActive *ntypes.Bool,
		isConfirmed *ntypes.Bool,
		isStaff *ntypes.Bool,
		isSuperuser *ntypes.Bool,
		lastLoginAt *time.Time,
		lastName *ntypes.String,
		password []byte,
		updatedAt *time.Time,
		updatedBy *ntypes.Int64,
		username *ntypes.String,
	) (*userEntity, error)
	RegistrationConfirmation(id int64, confirmationToken string) error
	IsGranted(id int64, permission charon.Permission) (bool, error)
	SetPermissions(id int64, permissions ...charon.Permission) (int64, int64, error)
}

func newUserRepository(dbPool *sql.DB) userProvider {
	return &userRepository{
		db:      dbPool,
		table:   tableUser,
		columns: tableUserColumns,
	}
}

// Create implements userProvider interface.
func (ur *userRepository) Create(username string, password []byte, firstName, lastName string, confirmationToken []byte, isSuperuser, isStaff, isActive, isConfirmed bool) (*userEntity, error) {
	if isSuperuser {
		isStaff = true
		isActive = true
		isConfirmed = true
	}

	entity := &userEntity{
		Username:          username,
		Password:          password,
		FirstName:         firstName,
		LastName:          lastName,
		ConfirmationToken: confirmationToken,
		IsSuperuser:       isSuperuser,
		IsStaff:           isStaff,
		IsActive:          isActive,
		IsConfirmed:       isConfirmed,
	}
	return ur.Insert(entity)
}

// CreateSuperuser implements userProvider interface.
func (ur *userRepository) CreateSuperuser(username string, password []byte, firstName, lastName string) (*userEntity, error) {
	return ur.Create(username, password, firstName, lastName, []byte(UserConfirmationTokenUsed), true, false, true, true)
}

// Count implements userProvider interface.
func (ur *userRepository) Count() (n int64, err error) {
	err = ur.db.QueryRow("SELECT COUNT(*) FROM " + tableUser).Scan(&n)

	return
}

func (ur *userRepository) RegistrationConfirmation(userID int64, confirmationToken string) error {
	query := `
		UPDATE ` + ur.table + `
		SET is_confirmed = true, is_active = true, updated_at = NOW(), confirmation_token = $1
		WHERE is_confirmed = false AND id = $2 AND confirmation_token = $3;
	`

	result, err := ur.db.Exec(
		query,
		UserConfirmationTokenUsed,
		userID,
		confirmationToken,
	)
	if err != nil {
		return err
	}

	_, err = result.RowsAffected()

	return err
}

// ChangePassword ...
func (ur *userRepository) ChangePassword(userID int64, password string) error {
	query := `
		UPDATE ` + ur.table + `
		SET password = $2, updated_at = NOW()
		WHERE id = $1;
	`

	result, err := ur.db.Exec(
		query,
		userID,
		password,
	)
	if err != nil {
		return err
	}
	_, err = result.RowsAffected()

	return err
}

// FindOneByUsername ...
func (ur *userRepository) FindOneByUsername(username string) (*userEntity, error) {
	return ur.findOneBy("username", username)
}

func (ur *userRepository) findOneBy(fieldName string, value interface{}) (*userEntity, error) {
	query := `
		SELECT ` + strings.Join(tableUserColumns, ",") + `
		FROM ` + ur.table + `
		WHERE ` + fieldName + ` = $1
		LIMIT 1
	`

	var user userEntity
	err := ur.db.QueryRow(query, value).Scan(
		&user.ConfirmationToken,
		&user.CreatedAt,
		&user.CreatedBy,
		&user.FirstName,
		&user.ID,
		&user.IsActive,
		&user.IsConfirmed,
		&user.IsStaff,
		&user.IsSuperuser,
		&user.LastLoginAt,
		&user.LastName,
		&user.Password,
		&user.UpdatedAt,
		&user.UpdatedBy,
		&user.Username,
	)

	if err != nil {
		return nil, err
	}

	return &user, nil
}

// UpdateLastLoginAt implements userProvider interface.
func (ur *userRepository) UpdateLastLoginAt(userID int64) (int64, error) {
	query := `
		UPDATE ` + ur.table + `
		SET ` + tableUserColumnLastLoginAt + ` = NOW()
		WHERE ` + tableUserColumnID + ` = $1
	`

	result, err := ur.db.Exec(
		query,
		userID,
	)
	if err != nil {
		return 0, err
	}

	return result.RowsAffected()
}

// Exists implements userProvider interface.
func (ur *userRepository) Exists(userID int64) (bool, error) {
	query := `
		SELECT EXISTS(
			SELECT 1
			FROM ` + ur.table + ` AS p
			WHERE ` + tableUserColumnID + ` = $1
		)
	`

	var exists bool
	if err := ur.db.QueryRow(query, userID).Scan(&exists); err != nil {
		return false, err
	}

	return exists, nil
}

// IsGranted implements userProvider interface.
func (ur *userRepository) IsGranted(id int64, p charon.Permission) (bool, error) {
	var exists bool
	subsystem, module, action := p.Split()
	if err := ur.db.QueryRow(isGrantedQuery(
		tableUserPermissions,
		tableUserPermissionsColumnUserID,
		tableUserPermissionsColumnPermissionSubsystem,
		tableUserPermissionsColumnPermissionModule,
		tableUserPermissionsColumnPermissionAction,
	), id, subsystem, module, action).Scan(&exists); err != nil {
		return false, err
	}

	return exists, nil
}

// SetPermissions implements userProvider interface.
func (ur *userRepository) SetPermissions(id int64, p ...charon.Permission) (int64, int64, error) {
	return setPermissions(ur.db, tableUserPermissions,
		tableUserPermissionsColumnUserID,
		tableUserPermissionsColumnPermissionSubsystem,
		tableUserPermissionsColumnPermissionModule,
		tableUserPermissionsColumnPermissionAction, id, p)
}

func createDummyTestUser(repo userProvider, hasher charon.PasswordHasher) (*userEntity, error) {
	password, err := hasher.Hash([]byte("test"))
	if err != nil {
		return nil, err
	}
	return repo.CreateSuperuser("test", password, "Test", "Test")
}
<|endoftext|>


<commit_before>package hcn

import (
	"encoding/json"
)

// EndpointPolicyType are the potential Policies that apply to Endpoints.
type EndpointPolicyType string

// EndpointPolicyType const
const (
	PortMapping   EndpointPolicyType = "PortMapping"
	ACL           EndpointPolicyType = "ACL"
	QOS           EndpointPolicyType = "QOS"
	L2Driver      EndpointPolicyType = "L2Driver"
	OutBoundNAT   EndpointPolicyType = "OutBoundNAT"
	SDNRoute      EndpointPolicyType = "SDNRoute"
	L4Proxy       EndpointPolicyType = "L4Proxy"
	L4WFPPROXY    EndpointPolicyType = "L4WFPPROXY"
	PortName      EndpointPolicyType = "PortName"
	EncapOverhead EndpointPolicyType = "EncapOverhead"
	IOV           EndpointPolicyType = "Iov"
	// Endpoint and Network have InterfaceConstraint and ProviderAddress
	NetworkProviderAddress     EndpointPolicyType = "ProviderAddress"
	NetworkInterfaceConstraint EndpointPolicyType = "InterfaceConstraint"
)

// EndpointPolicy is a collection of Policy settings for an Endpoint.
type EndpointPolicy struct {
	Type     EndpointPolicyType `json:""`
	Settings json.RawMessage    `json:",omitempty"`
}

// NetworkPolicyType are the potential Policies that apply to Networks.
type NetworkPolicyType string

// NetworkPolicyType const
const (
	SourceMacAddress    NetworkPolicyType = "SourceMacAddress"
	NetAdapterName      NetworkPolicyType = "NetAdapterName"
	VSwitchExtension    NetworkPolicyType = "VSwitchExtension"
	DrMacAddress        NetworkPolicyType = "DrMacAddress"
	AutomaticDNS        NetworkPolicyType = "AutomaticDNS"
	InterfaceConstraint NetworkPolicyType = "InterfaceConstraint"
	ProviderAddress     NetworkPolicyType = "ProviderAddress"
	RemoteSubnetRoute   NetworkPolicyType = "RemoteSubnetRoute"
	VxlanPort           NetworkPolicyType = "VxlanPort"
	HostRoute           NetworkPolicyType = "HostRoute"
	SetPolicy           NetworkPolicyType = "SetPolicy"
	NetworkL4Proxy      NetworkPolicyType = "L4Proxy"
	LayerConstraint     NetworkPolicyType = "LayerConstraint"
)

// NetworkPolicy is a collection of Policy settings for a Network.
type NetworkPolicy struct {
	Type     NetworkPolicyType `json:""`
	Settings json.RawMessage   `json:",omitempty"`
}

// SubnetPolicyType are the potential Policies that apply to Subnets.
type SubnetPolicyType string

// SubnetPolicyType const
const (
	VLAN SubnetPolicyType = "VLAN"
	VSID SubnetPolicyType = "VSID"
)

// SubnetPolicy is a collection of Policy settings for a Subnet.
type SubnetPolicy struct {
	Type     SubnetPolicyType `json:""`
	Settings json.RawMessage  `json:",omitempty"`
}

// NatFlags are flags for portmappings.
type NatFlags uint32

const (
	NatFlagsNone            NatFlags = 0
	NatFlagsLocalRoutedVip  NatFlags = 1
	NatFlagsIPv6            NatFlags = 2
)

/// Endpoint Policy objects

// PortMappingPolicySetting defines Port Mapping (NAT)
type PortMappingPolicySetting struct {
	Protocol     uint32   `json:",omitempty"` // EX: TCP = 6, UDP = 17
	InternalPort uint16   `json:",omitempty"`
	ExternalPort uint16   `json:",omitempty"`
	VIP          string   `json:",omitempty"`
	Flags        NatFlags `json:",omitempty"`
}

// ActionType associated with ACLs. Value is either Allow or Block.
type ActionType string

// DirectionType associated with ACLs. Value is either In or Out.
type DirectionType string

// RuleType associated with ACLs. Value is either Host (WFP) or Switch (VFP).
type RuleType string

const (
	// Allow traffic
	ActionTypeAllow ActionType = "Allow"
	// Block traffic
	ActionTypeBlock ActionType = "Block"

	// In is traffic coming to the Endpoint
	DirectionTypeIn DirectionType = "In"
	// Out is traffic leaving the Endpoint
	DirectionTypeOut DirectionType = "Out"

	// Host creates WFP (Windows Firewall) rules
	RuleTypeHost RuleType = "Host"
	// Switch creates VFP (Virtual Filter Platform) rules
	RuleTypeSwitch RuleType = "Switch"
)

// AclPolicySetting creates firewall rules on an endpoint
type AclPolicySetting struct {
	Protocols       string        `json:",omitempty"` // EX: 6 (TCP), 17 (UDP), 1 (ICMPv4), 58 (ICMPv6), 2 (IGMP)
	Action          ActionType    `json:","`
	Direction       DirectionType `json:","`
	LocalAddresses  string        `json:",omitempty"`
	RemoteAddresses string        `json:",omitempty"`
	LocalPorts      string        `json:",omitempty"`
	RemotePorts     string        `json:",omitempty"`
	RuleType        RuleType      `json:",omitempty"`
	Priority        uint16        `json:",omitempty"`
}

// QosPolicySetting sets Quality of Service bandwidth caps on an Endpoint.
type QosPolicySetting struct {
	MaximumOutgoingBandwidthInBytes uint64
}

// OutboundNatPolicySetting sets outbound Network Address Translation on an Endpoint.
type OutboundNatPolicySetting struct {
	VirtualIP    string   `json:",omitempty"`
	Exceptions   []string `json:",omitempty"`
	Destinations []string `json:",omitempty"`
	Flags        NatFlags `json:",omitempty"`
}

// SDNRoutePolicySetting sets SDN Route on an Endpoint.
type SDNRoutePolicySetting struct {
	DestinationPrefix string `json:",omitempty"`
	NextHop           string `json:",omitempty"`
	NeedEncap         bool   `json:",omitempty"`
}

// FiveTuple is nested in L4ProxyPolicySetting  for WFP support.
type FiveTuple struct {
	Protocols       string `json:",omitempty"`
	LocalAddresses  string `json:",omitempty"`
	RemoteAddresses string `json:",omitempty"`
	LocalPorts      string `json:",omitempty"`
	RemotePorts     string `json:",omitempty"`
	Priority        uint16 `json:",omitempty"`
}

// L4WfpProxyPolicySetting sets Layer-4 Proxy on an endpoint.
type L4WfpProxyPolicySetting struct {
	InboundProxyPort  string    `json:",omitempty"`
	OutboundProxyPort string    `json:",omitempty"`
	FilterTuple       FiveTuple `json:",omitempty"`
	UserSID           string    `json:",omitempty"`
}

// PortnameEndpointPolicySetting sets the port name for an endpoint.
type PortnameEndpointPolicySetting struct {
	Name string `json:",omitempty"`
}

// EncapOverheadEndpointPolicySetting sets the encap overhead for an endpoint.
type EncapOverheadEndpointPolicySetting struct {
	Overhead uint16 `json:",omitempty"`
}

// IovPolicySetting sets the Iov settings for an endpoint.
type IovPolicySetting struct {
	IovOffloadWeight    uint32 `json:",omitempty"`
	QueuePairsRequested uint32 `json:",omitempty"`
	InterruptModeration uint32 `json:",omitempty"`
}

/// Endpoint and Network Policy objects

// ProviderAddressEndpointPolicySetting sets the PA for an endpoint.
type ProviderAddressEndpointPolicySetting struct {
	ProviderAddress string `json:",omitempty"`
}

// InterfaceConstraintPolicySetting limits an Endpoint or Network to a specific Nic.
type InterfaceConstraintPolicySetting struct {
	InterfaceGuid        string `json:",omitempty"`
	InterfaceLuid        uint64 `json:",omitempty"`
	InterfaceIndex       uint32 `json:",omitempty"`
	InterfaceMediaType   uint32 `json:",omitempty"`
	InterfaceAlias       string `json:",omitempty"`
	InterfaceDescription string `json:",omitempty"`
}

/// Network Policy objects

// SourceMacAddressNetworkPolicySetting sets source MAC for a network.
type SourceMacAddressNetworkPolicySetting struct {
	SourceMacAddress string `json:",omitempty"`
}

// NetAdapterNameNetworkPolicySetting sets network adapter of a network.
type NetAdapterNameNetworkPolicySetting struct {
	NetworkAdapterName string `json:",omitempty"`
}

// VSwitchExtensionNetworkPolicySetting enables/disabled VSwitch extensions for a network.
type VSwitchExtensionNetworkPolicySetting struct {
	ExtensionID string `json:",omitempty"`
	Enable      bool   `json:",omitempty"`
}

// DrMacAddressNetworkPolicySetting sets the DR MAC for a network.
type DrMacAddressNetworkPolicySetting struct {
	Address string `json:",omitempty"`
}

// AutomaticDNSNetworkPolicySetting enables/disables automatic DNS on a network.
type AutomaticDNSNetworkPolicySetting struct {
	Enable bool `json:",omitempty"`
}

type LayerConstraintNetworkPolicySetting struct {
	LayerId string `json:",omitempty"`
}

/// Subnet Policy objects

// VlanPolicySetting isolates a subnet with VLAN tagging.
type VlanPolicySetting struct {
	IsolationId uint32 `json:","`
}

// VsidPolicySetting isolates a subnet with VSID tagging.
type VsidPolicySetting struct {
	IsolationId uint32 `json:","`
}

// RemoteSubnetRoutePolicySetting creates remote subnet route rules on a network
type RemoteSubnetRoutePolicySetting struct {
	DestinationPrefix           string
	IsolationId                 uint16
	ProviderAddress             string
	DistributedRouterMacAddress string
}

// SetPolicyTypes associated with SetPolicy. Value is IPSET.
type SetPolicyType string

const (
	SetPolicyTypeIpSet SetPolicyType = "IPSET"
)

// SetPolicySetting creates IPSets on network
type SetPolicySetting struct {
	Id     string
	Name   string
	Type   SetPolicyType
	Values string
}

// VxlanPortPolicySetting allows configuring the VXLAN TCP port
type VxlanPortPolicySetting struct {
	Port uint16
}

// ProtocolType associated with L4ProxyPolicy
type ProtocolType uint32

const (
	ProtocolTypeUnknown ProtocolType = 0
	ProtocolTypeICMPv4  ProtocolType = 1
	ProtocolTypeIGMP    ProtocolType = 2
	ProtocolTypeTCP     ProtocolType = 6
	ProtocolTypeUDP     ProtocolType = 17
	ProtocolTypeICMPv6  ProtocolType = 58
)

//L4ProxyPolicySetting applies proxy policy on network/endpoint
type L4ProxyPolicySetting struct {
	IP          string       `json:",omitempty"`
	Port        string       `json:",omitempty"`
	Protocol    ProtocolType `json:",omitempty"`
	Exceptions  []string     `json:",omitempty"`
	Destination string
	OutboundNAT bool `json:",omitempty"`
}
<commit_msg>CR feedback<commit_after>package hcn

import (
	"encoding/json"
)

// EndpointPolicyType are the potential Policies that apply to Endpoints.
type EndpointPolicyType string

// EndpointPolicyType const
const (
	PortMapping   EndpointPolicyType = "PortMapping"
	ACL           EndpointPolicyType = "ACL"
	QOS           EndpointPolicyType = "QOS"
	L2Driver      EndpointPolicyType = "L2Driver"
	OutBoundNAT   EndpointPolicyType = "OutBoundNAT"
	SDNRoute      EndpointPolicyType = "SDNRoute"
	L4Proxy       EndpointPolicyType = "L4Proxy"
	L4WFPPROXY    EndpointPolicyType = "L4WFPPROXY"
	PortName      EndpointPolicyType = "PortName"
	EncapOverhead EndpointPolicyType = "EncapOverhead"
	IOV           EndpointPolicyType = "Iov"
	// Endpoint and Network have InterfaceConstraint and ProviderAddress
	NetworkProviderAddress     EndpointPolicyType = "ProviderAddress"
	NetworkInterfaceConstraint EndpointPolicyType = "InterfaceConstraint"
)

// EndpointPolicy is a collection of Policy settings for an Endpoint.
type EndpointPolicy struct {
	Type     EndpointPolicyType `json:""`
	Settings json.RawMessage    `json:",omitempty"`
}

// NetworkPolicyType are the potential Policies that apply to Networks.
type NetworkPolicyType string

// NetworkPolicyType const
const (
	SourceMacAddress    NetworkPolicyType = "SourceMacAddress"
	NetAdapterName      NetworkPolicyType = "NetAdapterName"
	VSwitchExtension    NetworkPolicyType = "VSwitchExtension"
	DrMacAddress        NetworkPolicyType = "DrMacAddress"
	AutomaticDNS        NetworkPolicyType = "AutomaticDNS"
	InterfaceConstraint NetworkPolicyType = "InterfaceConstraint"
	ProviderAddress     NetworkPolicyType = "ProviderAddress"
	RemoteSubnetRoute   NetworkPolicyType = "RemoteSubnetRoute"
	VxlanPort           NetworkPolicyType = "VxlanPort"
	HostRoute           NetworkPolicyType = "HostRoute"
	SetPolicy           NetworkPolicyType = "SetPolicy"
	NetworkL4Proxy      NetworkPolicyType = "L4Proxy"
	LayerConstraint     NetworkPolicyType = "LayerConstraint"
)

// NetworkPolicy is a collection of Policy settings for a Network.
type NetworkPolicy struct {
	Type     NetworkPolicyType `json:""`
	Settings json.RawMessage   `json:",omitempty"`
}

// SubnetPolicyType are the potential Policies that apply to Subnets.
type SubnetPolicyType string

// SubnetPolicyType const
const (
	VLAN SubnetPolicyType = "VLAN"
	VSID SubnetPolicyType = "VSID"
)

// SubnetPolicy is a collection of Policy settings for a Subnet.
type SubnetPolicy struct {
	Type     SubnetPolicyType `json:""`
	Settings json.RawMessage  `json:",omitempty"`
}

// NatFlags are flags for portmappings.
type NatFlags uint32

const (
	NatFlagsNone            NatFlags = iota
	NatFlagsLocalRoutedVip
	NatFlagsIPv6
)

/// Endpoint Policy objects

// PortMappingPolicySetting defines Port Mapping (NAT)
type PortMappingPolicySetting struct {
	Protocol     uint32   `json:",omitempty"` // EX: TCP = 6, UDP = 17
	InternalPort uint16   `json:",omitempty"`
	ExternalPort uint16   `json:",omitempty"`
	VIP          string   `json:",omitempty"`
	Flags        NatFlags `json:",omitempty"`
}

// ActionType associated with ACLs. Value is either Allow or Block.
type ActionType string

// DirectionType associated with ACLs. Value is either In or Out.
type DirectionType string

// RuleType associated with ACLs. Value is either Host (WFP) or Switch (VFP).
type RuleType string

const (
	// Allow traffic
	ActionTypeAllow ActionType = "Allow"
	// Block traffic
	ActionTypeBlock ActionType = "Block"

	// In is traffic coming to the Endpoint
	DirectionTypeIn DirectionType = "In"
	// Out is traffic leaving the Endpoint
	DirectionTypeOut DirectionType = "Out"

	// Host creates WFP (Windows Firewall) rules
	RuleTypeHost RuleType = "Host"
	// Switch creates VFP (Virtual Filter Platform) rules
	RuleTypeSwitch RuleType = "Switch"
)

// AclPolicySetting creates firewall rules on an endpoint
type AclPolicySetting struct {
	Protocols       string        `json:",omitempty"` // EX: 6 (TCP), 17 (UDP), 1 (ICMPv4), 58 (ICMPv6), 2 (IGMP)
	Action          ActionType    `json:","`
	Direction       DirectionType `json:","`
	LocalAddresses  string        `json:",omitempty"`
	RemoteAddresses string        `json:",omitempty"`
	LocalPorts      string        `json:",omitempty"`
	RemotePorts     string        `json:",omitempty"`
	RuleType        RuleType      `json:",omitempty"`
	Priority        uint16        `json:",omitempty"`
}

// QosPolicySetting sets Quality of Service bandwidth caps on an Endpoint.
type QosPolicySetting struct {
	MaximumOutgoingBandwidthInBytes uint64
}

// OutboundNatPolicySetting sets outbound Network Address Translation on an Endpoint.
type OutboundNatPolicySetting struct {
	VirtualIP    string   `json:",omitempty"`
	Exceptions   []string `json:",omitempty"`
	Destinations []string `json:",omitempty"`
	Flags        NatFlags `json:",omitempty"`
}

// SDNRoutePolicySetting sets SDN Route on an Endpoint.
type SDNRoutePolicySetting struct {
	DestinationPrefix string `json:",omitempty"`
	NextHop           string `json:",omitempty"`
	NeedEncap         bool   `json:",omitempty"`
}

// FiveTuple is nested in L4ProxyPolicySetting  for WFP support.
type FiveTuple struct {
	Protocols       string `json:",omitempty"`
	LocalAddresses  string `json:",omitempty"`
	RemoteAddresses string `json:",omitempty"`
	LocalPorts      string `json:",omitempty"`
	RemotePorts     string `json:",omitempty"`
	Priority        uint16 `json:",omitempty"`
}

// L4WfpProxyPolicySetting sets Layer-4 Proxy on an endpoint.
type L4WfpProxyPolicySetting struct {
	InboundProxyPort  string    `json:",omitempty"`
	OutboundProxyPort string    `json:",omitempty"`
	FilterTuple       FiveTuple `json:",omitempty"`
	UserSID           string    `json:",omitempty"`
}

// PortnameEndpointPolicySetting sets the port name for an endpoint.
type PortnameEndpointPolicySetting struct {
	Name string `json:",omitempty"`
}

// EncapOverheadEndpointPolicySetting sets the encap overhead for an endpoint.
type EncapOverheadEndpointPolicySetting struct {
	Overhead uint16 `json:",omitempty"`
}

// IovPolicySetting sets the Iov settings for an endpoint.
type IovPolicySetting struct {
	IovOffloadWeight    uint32 `json:",omitempty"`
	QueuePairsRequested uint32 `json:",omitempty"`
	InterruptModeration uint32 `json:",omitempty"`
}

/// Endpoint and Network Policy objects

// ProviderAddressEndpointPolicySetting sets the PA for an endpoint.
type ProviderAddressEndpointPolicySetting struct {
	ProviderAddress string `json:",omitempty"`
}

// InterfaceConstraintPolicySetting limits an Endpoint or Network to a specific Nic.
type InterfaceConstraintPolicySetting struct {
	InterfaceGuid        string `json:",omitempty"`
	InterfaceLuid        uint64 `json:",omitempty"`
	InterfaceIndex       uint32 `json:",omitempty"`
	InterfaceMediaType   uint32 `json:",omitempty"`
	InterfaceAlias       string `json:",omitempty"`
	InterfaceDescription string `json:",omitempty"`
}

/// Network Policy objects

// SourceMacAddressNetworkPolicySetting sets source MAC for a network.
type SourceMacAddressNetworkPolicySetting struct {
	SourceMacAddress string `json:",omitempty"`
}

// NetAdapterNameNetworkPolicySetting sets network adapter of a network.
type NetAdapterNameNetworkPolicySetting struct {
	NetworkAdapterName string `json:",omitempty"`
}

// VSwitchExtensionNetworkPolicySetting enables/disabled VSwitch extensions for a network.
type VSwitchExtensionNetworkPolicySetting struct {
	ExtensionID string `json:",omitempty"`
	Enable      bool   `json:",omitempty"`
}

// DrMacAddressNetworkPolicySetting sets the DR MAC for a network.
type DrMacAddressNetworkPolicySetting struct {
	Address string `json:",omitempty"`
}

// AutomaticDNSNetworkPolicySetting enables/disables automatic DNS on a network.
type AutomaticDNSNetworkPolicySetting struct {
	Enable bool `json:",omitempty"`
}

type LayerConstraintNetworkPolicySetting struct {
	LayerId string `json:",omitempty"`
}

/// Subnet Policy objects

// VlanPolicySetting isolates a subnet with VLAN tagging.
type VlanPolicySetting struct {
	IsolationId uint32 `json:","`
}

// VsidPolicySetting isolates a subnet with VSID tagging.
type VsidPolicySetting struct {
	IsolationId uint32 `json:","`
}

// RemoteSubnetRoutePolicySetting creates remote subnet route rules on a network
type RemoteSubnetRoutePolicySetting struct {
	DestinationPrefix           string
	IsolationId                 uint16
	ProviderAddress             string
	DistributedRouterMacAddress string
}

// SetPolicyTypes associated with SetPolicy. Value is IPSET.
type SetPolicyType string

const (
	SetPolicyTypeIpSet SetPolicyType = "IPSET"
)

// SetPolicySetting creates IPSets on network
type SetPolicySetting struct {
	Id     string
	Name   string
	Type   SetPolicyType
	Values string
}

// VxlanPortPolicySetting allows configuring the VXLAN TCP port
type VxlanPortPolicySetting struct {
	Port uint16
}

// ProtocolType associated with L4ProxyPolicy
type ProtocolType uint32

const (
	ProtocolTypeUnknown ProtocolType = 0
	ProtocolTypeICMPv4  ProtocolType = 1
	ProtocolTypeIGMP    ProtocolType = 2
	ProtocolTypeTCP     ProtocolType = 6
	ProtocolTypeUDP     ProtocolType = 17
	ProtocolTypeICMPv6  ProtocolType = 58
)

//L4ProxyPolicySetting applies proxy policy on network/endpoint
type L4ProxyPolicySetting struct {
	IP          string       `json:",omitempty"`
	Port        string       `json:",omitempty"`
	Protocol    ProtocolType `json:",omitempty"`
	Exceptions  []string     `json:",omitempty"`
	Destination string
	OutboundNAT bool `json:",omitempty"`
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/sirupsen/logrus"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/zfjagann/golang-ring"
	"net/http"
	"sync"
	"time"
)

type Api struct {
	Messages ring.Ring
	sync.RWMutex
	*config.BridgeConfig
}

type ApiMessage struct {
	Text     string `json:"text"`
	Username string `json:"username"`
	UserID   string `json:"userid"`
	Avatar   string `json:"avatar"`
	Gateway  string `json:"gateway"`
}

var flog *log.Entry
var protocol = "api"

func init() {
	flog = log.WithFields(log.Fields{"prefix": protocol})
}

func New(cfg *config.BridgeConfig) *Api {
	b := &Api{BridgeConfig: cfg}
	e := echo.New()
	b.Messages = ring.Ring{}
	b.Messages.SetCapacity(b.Config.Buffer)
	if b.Config.Token != "" {
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
			return key == b.Config.Token, nil
		}))
	}
	e.GET("/api/messages", b.handleMessages)
	e.GET("/api/stream", b.handleStream)
	e.POST("/api/message", b.handlePostMessage)
	go func() {
		flog.Fatal(e.Start(b.Config.BindAddress))
	}()
	return b
}

func (b *Api) Connect() error {
	return nil
}
func (b *Api) Disconnect() error {
	return nil

}
func (b *Api) JoinChannel(channel config.ChannelInfo) error {
	return nil

}

func (b *Api) Send(msg config.Message) (string, error) {
	b.Lock()
	defer b.Unlock()
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Messages.Enqueue(&msg)
	return "", nil
}

func (b *Api) handlePostMessage(c echo.Context) error {
	message := config.Message{}
	if err := c.Bind(&message); err != nil {
		return err
	}
	// these values are fixed
	message.Channel = "api"
	message.Protocol = "api"
	message.Account = b.Account
	message.ID = ""
	message.Timestamp = time.Now()
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
	b.Remote <- message
	return c.JSON(http.StatusOK, message)
}

func (b *Api) handleMessages(c echo.Context) error {
	b.Lock()
	defer b.Unlock()
	c.JSONPretty(http.StatusOK, b.Messages.Values(), " ")
	b.Messages = ring.Ring{}
	return nil
}

func (b *Api) handleStream(c echo.Context) error {
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	c.Response().WriteHeader(http.StatusOK)
	closeNotifier := c.Response().CloseNotify()
	for {
		select {
		case <-closeNotifier:
			return nil
		default:
			msg := b.Messages.Dequeue()
			if msg != nil {
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
					return err
				}
				c.Response().Flush()
			}
			time.Sleep(200 * time.Millisecond)
		}
	}
}
<commit_msg>Disable echo banner and output (api)<commit_after>package api

import (
	"encoding/json"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	log "github.com/sirupsen/logrus"
	"github.com/zfjagann/golang-ring"
	"net/http"
	"sync"
	"time"
)

type Api struct {
	Messages ring.Ring
	sync.RWMutex
	*config.BridgeConfig
}

type ApiMessage struct {
	Text     string `json:"text"`
	Username string `json:"username"`
	UserID   string `json:"userid"`
	Avatar   string `json:"avatar"`
	Gateway  string `json:"gateway"`
}

var flog *log.Entry
var protocol = "api"

func init() {
	flog = log.WithFields(log.Fields{"prefix": protocol})
}

func New(cfg *config.BridgeConfig) *Api {
	b := &Api{BridgeConfig: cfg}
	e := echo.New()
	e.HideBanner = true
	e.HidePort = true
	b.Messages = ring.Ring{}
	b.Messages.SetCapacity(b.Config.Buffer)
	if b.Config.Token != "" {
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
			return key == b.Config.Token, nil
		}))
	}
	e.GET("/api/messages", b.handleMessages)
	e.GET("/api/stream", b.handleStream)
	e.POST("/api/message", b.handlePostMessage)
	go func() {
		if b.Config.BindAddress == "" {
			flog.Fatalf("No BindAddress configured.")
		}
		flog.Infof("Listening on %s", b.Config.BindAddress)
		flog.Fatal(e.Start(b.Config.BindAddress))
	}()
	return b
}

func (b *Api) Connect() error {
	return nil
}
func (b *Api) Disconnect() error {
	return nil

}
func (b *Api) JoinChannel(channel config.ChannelInfo) error {
	return nil

}

func (b *Api) Send(msg config.Message) (string, error) {
	b.Lock()
	defer b.Unlock()
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Messages.Enqueue(&msg)
	return "", nil
}

func (b *Api) handlePostMessage(c echo.Context) error {
	message := config.Message{}
	if err := c.Bind(&message); err != nil {
		return err
	}
	// these values are fixed
	message.Channel = "api"
	message.Protocol = "api"
	message.Account = b.Account
	message.ID = ""
	message.Timestamp = time.Now()
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
	b.Remote <- message
	return c.JSON(http.StatusOK, message)
}

func (b *Api) handleMessages(c echo.Context) error {
	b.Lock()
	defer b.Unlock()
	c.JSONPretty(http.StatusOK, b.Messages.Values(), " ")
	b.Messages = ring.Ring{}
	return nil
}

func (b *Api) handleStream(c echo.Context) error {
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	c.Response().WriteHeader(http.StatusOK)
	closeNotifier := c.Response().CloseNotify()
	for {
		select {
		case <-closeNotifier:
			return nil
		default:
			msg := b.Messages.Dequeue()
			if msg != nil {
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
					return err
				}
				c.Response().Flush()
			}
			time.Sleep(200 * time.Millisecond)
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/zfjagann/golang-ring"
	"net/http"
	"sync"
	"time"
)

type Api struct {
	Messages ring.Ring
	sync.RWMutex
	*config.BridgeConfig
}

type ApiMessage struct {
	Text     string `json:"text"`
	Username string `json:"username"`
	UserID   string `json:"userid"`
	Avatar   string `json:"avatar"`
	Gateway  string `json:"gateway"`
}

var flog *log.Entry
var protocol = "api"

func init() {
	flog = log.WithFields(log.Fields{"module": protocol})
}

func New(cfg *config.BridgeConfig) *Api {
	b := &Api{BridgeConfig: cfg}
	e := echo.New()
	b.Messages = ring.Ring{}
	b.Messages.SetCapacity(b.Config.Buffer)
	if b.Config.Token != "" {
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
			return key == b.Config.Token, nil
		}))
	}
	e.GET("/api/messages", b.handleMessages)
	e.GET("/api/stream", b.handleStream)
	e.POST("/api/message", b.handlePostMessage)
	go func() {
		flog.Fatal(e.Start(b.Config.BindAddress))
	}()
	return b
}

func (b *Api) Connect() error {
	return nil
}
func (b *Api) Disconnect() error {
	return nil

}
func (b *Api) JoinChannel(channel config.ChannelInfo) error {
	return nil

}

func (b *Api) Send(msg config.Message) (string, error) {
	b.Lock()
	defer b.Unlock()
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Messages.Enqueue(&msg)
	return "", nil
}

func (b *Api) handlePostMessage(c echo.Context) error {
	message := &ApiMessage{}
	if err := c.Bind(message); err != nil {
		return err
	}
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
	b.Remote <- config.Message{
		Text:     message.Text,
		Username: message.Username,
		UserID:   message.UserID,
		Channel:  "api",
		Avatar:   message.Avatar,
		Account:  b.Account,
		Gateway:  message.Gateway,
		Protocol: "api",
	}
	return c.JSON(http.StatusOK, message)
}

func (b *Api) handleMessages(c echo.Context) error {
	b.Lock()
	defer b.Unlock()
	c.JSONPretty(http.StatusOK, b.Messages.Values(), " ")
	b.Messages = ring.Ring{}
	return nil
}

func (b *Api) handleStream(c echo.Context) error {
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	c.Response().WriteHeader(http.StatusOK)
	closeNotifier := c.Response().CloseNotify()
	for {
		select {
		case <-closeNotifier:
			return nil
		default:
			msg := b.Messages.Dequeue()
			if msg != nil {
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
					return err
				}
				c.Response().Flush()
			}
			time.Sleep(200 * time.Millisecond)
		}
	}
}
<commit_msg>Give api access to whole config.Message (and events). Closes #374<commit_after>package api

import (
	"encoding/json"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/zfjagann/golang-ring"
	"net/http"
	"sync"
	"time"
)

type Api struct {
	Messages ring.Ring
	sync.RWMutex
	*config.BridgeConfig
}

type ApiMessage struct {
	Text     string `json:"text"`
	Username string `json:"username"`
	UserID   string `json:"userid"`
	Avatar   string `json:"avatar"`
	Gateway  string `json:"gateway"`
}

var flog *log.Entry
var protocol = "api"

func init() {
	flog = log.WithFields(log.Fields{"module": protocol})
}

func New(cfg *config.BridgeConfig) *Api {
	b := &Api{BridgeConfig: cfg}
	e := echo.New()
	b.Messages = ring.Ring{}
	b.Messages.SetCapacity(b.Config.Buffer)
	if b.Config.Token != "" {
		e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
			return key == b.Config.Token, nil
		}))
	}
	e.GET("/api/messages", b.handleMessages)
	e.GET("/api/stream", b.handleStream)
	e.POST("/api/message", b.handlePostMessage)
	go func() {
		flog.Fatal(e.Start(b.Config.BindAddress))
	}()
	return b
}

func (b *Api) Connect() error {
	return nil
}
func (b *Api) Disconnect() error {
	return nil

}
func (b *Api) JoinChannel(channel config.ChannelInfo) error {
	return nil

}

func (b *Api) Send(msg config.Message) (string, error) {
	b.Lock()
	defer b.Unlock()
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Messages.Enqueue(&msg)
	return "", nil
}

func (b *Api) handlePostMessage(c echo.Context) error {
	message := config.Message{}
	if err := c.Bind(&message); err != nil {
		return err
	}
	// these values are fixed
	message.Channel = "api"
	message.Protocol = "api"
	message.Account = b.Account
	message.ID = ""
	message.Timestamp = time.Now()
	flog.Debugf("Sending message from %s on %s to gateway", message.Username, "api")
	b.Remote <- message
	return c.JSON(http.StatusOK, message)
}

func (b *Api) handleMessages(c echo.Context) error {
	b.Lock()
	defer b.Unlock()
	c.JSONPretty(http.StatusOK, b.Messages.Values(), " ")
	b.Messages = ring.Ring{}
	return nil
}

func (b *Api) handleStream(c echo.Context) error {
	c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	c.Response().WriteHeader(http.StatusOK)
	closeNotifier := c.Response().CloseNotify()
	for {
		select {
		case <-closeNotifier:
			return nil
		default:
			msg := b.Messages.Dequeue()
			if msg != nil {
				if err := json.NewEncoder(c.Response()).Encode(msg); err != nil {
					return err
				}
				c.Response().Flush()
			}
			time.Sleep(200 * time.Millisecond)
		}
	}
}
<|endoftext|>


<commit_before>package TF2RconWrapper

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"time"
)

// RconChatListener maintains an UDP server that receives redirected chat messages from TF2 servers
type RconChatListener struct {
	conn    *net.UDPConn
	servers map[string]*ServerListener
	exit    chan bool
	addr    *net.UDPAddr
	localip string
	port    string
	rng     *rand.Rand
}

// NewRconChatListener builds a new RconChatListener. Its arguments are localip (the ip of this server) and
// port (the port the listener will use)
func NewRconChatListener(localip, port string) (*RconChatListener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	exit := make(chan bool)
	servers := make(map[string]*ServerListener)

	rng := rand.New(rand.NewSource(time.Now().Unix()))

	listener := &RconChatListener{nil, servers, exit, addr, localip, port, rng}
	listener.startListening()
	return listener, nil
}

func (r *RconChatListener) startListening() {
	conn, err := net.ListenUDP("udp", r.addr)
	r.conn = conn
	if err != nil {
		log.Println(err)
		return
	}

	go r.readStrings()
}

func (r *RconChatListener) readStrings() {
	buff := make([]byte, 4096)

	for {
		select {
		case <-r.exit:
			return
		default:
			r.conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
			n, _, err := r.conn.ReadFromUDP(buff)
			if err != nil {
				if typedErr, ok := err.(*net.OpError); ok && typedErr.Timeout() {
					continue
				}

				fmt.Println("Error receiving server chat data: ", err)
			}

			message := buff[0:n]

			messageObj, secret, err := proccessMessage(message)

			if err != nil {
				log.Println(err)
				continue
			}

			s, ok := r.servers[secret]

			if !ok {
				log.Println("Received chat info from an unregistered TF2 server")
				continue
			}

			s.messages <- messageObj
		}
	}
}

// Close stops the RconChatListener
func (r *RconChatListener) Close() {
	r.exit <- true
	r.conn.Close()
}

// CreateServerListener creates a ServerListener that receives chat messages from a
// particular TF2 server
func (r *RconChatListener) CreateServerListener(m *TF2RconConnection) *ServerListener {

	secret := strconv.Itoa(r.rng.Intn(999998) + 1)

	_, ok := r.servers[secret]
	for ok {
		secret = strconv.Itoa(r.rng.Intn(999998) + 1)
		_, ok = r.servers[secret]
	}

	s := &ServerListener{make(chan ChatMessage), m.host, secret, r}

	r.servers[secret] = s

	go func() {
		m.Query("sv_logsecret " + secret)
		m.RedirectLogs(r.localip, r.port)
	}()

	return s
}

// ServerListener represents a listener that receives chat messages from a particular
// TF2 server. It's built and managed by an RconChatListener instance.
type ServerListener struct {
	messages chan ChatMessage
	host     string
	secret   string
	listener *RconChatListener
}

// GetNext blocks until a chat message is received and then returns it
func (s *ServerListener) GetNext() ChatMessage {
	return <-s.messages
}
<commit_msg>struct ServerListener: Make field Messages public.<commit_after>package TF2RconWrapper

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"time"
)

// RconChatListener maintains an UDP server that receives redirected chat messages from TF2 servers
type RconChatListener struct {
	conn    *net.UDPConn
	servers map[string]*ServerListener
	exit    chan bool
	addr    *net.UDPAddr
	localip string
	port    string
	rng     *rand.Rand
}

// NewRconChatListener builds a new RconChatListener. Its arguments are localip (the ip of this server) and
// port (the port the listener will use)
func NewRconChatListener(localip, port string) (*RconChatListener, error) {
	addr, err := net.ResolveUDPAddr("udp", ":"+port)
	if err != nil {
		return nil, err
	}

	exit := make(chan bool)
	servers := make(map[string]*ServerListener)

	rng := rand.New(rand.NewSource(time.Now().Unix()))

	listener := &RconChatListener{nil, servers, exit, addr, localip, port, rng}
	listener.startListening()
	return listener, nil
}

func (r *RconChatListener) startListening() {
	conn, err := net.ListenUDP("udp", r.addr)
	r.conn = conn
	if err != nil {
		log.Println(err)
		return
	}

	go r.readStrings()
}

func (r *RconChatListener) readStrings() {
	buff := make([]byte, 4096)

	for {
		select {
		case <-r.exit:
			return
		default:
			r.conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
			n, _, err := r.conn.ReadFromUDP(buff)
			if err != nil {
				if typedErr, ok := err.(*net.OpError); ok && typedErr.Timeout() {
					continue
				}

				fmt.Println("Error receiving server chat data: ", err)
			}

			message := buff[0:n]

			messageObj, secret, err := proccessMessage(message)

			if err != nil {
				log.Println(err)
				continue
			}

			s, ok := r.servers[secret]

			if !ok {
				log.Println("Received chat info from an unregistered TF2 server")
				continue
			}

			s.Messages <- messageObj
		}
	}
}

// Close stops the RconChatListener
func (r *RconChatListener) Close() {
	r.exit <- true
	r.conn.Close()
}

// CreateServerListener creates a ServerListener that receives chat messages from a
// particular TF2 server
func (r *RconChatListener) CreateServerListener(m *TF2RconConnection) *ServerListener {

	secret := strconv.Itoa(r.rng.Intn(999998) + 1)

	_, ok := r.servers[secret]
	for ok {
		secret = strconv.Itoa(r.rng.Intn(999998) + 1)
		_, ok = r.servers[secret]
	}

	s := &ServerListener{make(chan ChatMessage), m.host, secret, r}

	r.servers[secret] = s

	go func() {
		m.Query("sv_logsecret " + secret)
		m.RedirectLogs(r.localip, r.port)
	}()

	return s
}

// ServerListener represents a listener that receives chat messages from a particular
// TF2 server. It's built and managed by an RconChatListener instance.
type ServerListener struct {
	Messages chan ChatMessage
	host     string
	secret   string
	listener *RconChatListener
}
<|endoftext|>


<commit_before>package gomatrixserverlib

import (
	"encoding/json"
	"reflect"
	"strings"

	"github.com/tidwall/sjson"
)

// HeaderedEventHeader contains header fields for an event that contains
// additional metadata, e.g. room version. IMPORTANT NOTE: All fields in
// this struct must have a "json:" name tag or otherwise the reflection
// code for marshalling and unmarshalling headered events will not work.
// They must be unique and not overlap with a name tag from the Event
// struct or otherwise panics may occur, so header  name tags are instead
// prefixed with an underscore.
type EventHeader struct {
	RoomVersion RoomVersion `json:"_room_version,omitempty"`
}

// HeaderedEvent is a wrapper around an Event that contains information
// about the room version. All header fields will be added into the event
// when marshalling into JSON and will be separated out when unmarshalling.
type HeaderedEvent struct {
	EventHeader
	Event
}

// Unwrap extracts the event object from the headered event.
func (e *HeaderedEvent) Unwrap() Event {
	if e.RoomVersion == "" {
		// TODO: Perhaps return an error here instead of panicing
		panic("gomatrixserverlib: malformed HeaderedEvent doesn't contain room version")
	}
	event := e.Event
	event.roomVersion = e.RoomVersion
	return event
}

// UnwrapEventHeaders unwraps an array of headered events.
func UnwrapEventHeaders(in []HeaderedEvent) []Event {
	result := make([]Event, len(in))
	for i := range in {
		result[i] = in[i].Event
	}
	return result
}

// UnmarshalJSON implements json.Unmarshaller
func (e *HeaderedEvent) UnmarshalJSON(data []byte) error {
	var err error
	// First extract the headers from the JSON.
	var m EventHeader
	if err = json.Unmarshal(data, &m); err != nil {
		return err
	}
	e.EventHeader = m
	// Now strip any of the header fields from the JSON input data.
	fields := reflect.TypeOf(e.EventHeader)
	for i := 0; i < fields.NumField(); i++ {
		tag := strings.Split(fields.Field(i).Tag.Get("json"), ",")[0]
		if data, err = sjson.DeleteBytes(data, tag); err != nil {
			return err
		}
	}
	// Get the event field format.
	eventFormat, err := m.RoomVersion.EventFormat()
	if err != nil {
		return err
	}
	// Check what the room version is and prepare the Event struct for
	// that specific version type.
	switch eventFormat {
	case EventFormatV1:
		e.fields = eventFormatV1Fields{}
	case EventFormatV2:
		e.fields = eventFormatV2Fields{}
	default:
		return UnsupportedRoomVersionError{m.RoomVersion}
	}
	// Finally, unmarshal the remaining event JSON (less the headers)
	// into the event struct.
	if e.Event, err = NewEventFromTrustedJSON(data, true, m.RoomVersion); err != nil {
		return err
	}
	// At this point unmarshalling is complete.
	return nil
}

// MarshalJSON implements json.Marshaller
func (e HeaderedEvent) MarshalJSON() ([]byte, error) {
	var err error
	// First marshal the event struct itself.
	content := e.Event.JSON()
	// Now jump through the fields of the header struct and add them
	// in separately. This is needed because of the way that Go handles
	// function overloading on embedded types, since Event also
	// implements custom marshalling and unmarshalling functions.
	// Doing this also ensures the least number of changes to Event
	// references elsewhere.
	fields := reflect.TypeOf(e.EventHeader)
	values := reflect.ValueOf(e.EventHeader)
	for i := 0; i < fields.NumField(); i++ {
		tag := strings.Split(fields.Field(i).Tag.Get("json"), ",")[0]
		if content, err = sjson.SetBytes(
			content, tag,
			values.Field(i).Interface(),
		); err != nil {
			return []byte{}, err
		}
	}
	// Return the newly marshalled JSON.
	return content, nil
}

type UnexpectedHeaderedEvent struct{}

func (u UnexpectedHeaderedEvent) Error() string {
	return "unexpected headered event"
}
<commit_msg>don't mark headered events as redacted<commit_after>package gomatrixserverlib

import (
	"encoding/json"
	"reflect"
	"strings"

	"github.com/tidwall/sjson"
)

// HeaderedEventHeader contains header fields for an event that contains
// additional metadata, e.g. room version. IMPORTANT NOTE: All fields in
// this struct must have a "json:" name tag or otherwise the reflection
// code for marshalling and unmarshalling headered events will not work.
// They must be unique and not overlap with a name tag from the Event
// struct or otherwise panics may occur, so header  name tags are instead
// prefixed with an underscore.
type EventHeader struct {
	RoomVersion RoomVersion `json:"_room_version,omitempty"`
}

// HeaderedEvent is a wrapper around an Event that contains information
// about the room version. All header fields will be added into the event
// when marshalling into JSON and will be separated out when unmarshalling.
type HeaderedEvent struct {
	EventHeader
	Event
}

// Unwrap extracts the event object from the headered event.
func (e *HeaderedEvent) Unwrap() Event {
	if e.RoomVersion == "" {
		// TODO: Perhaps return an error here instead of panicing
		panic("gomatrixserverlib: malformed HeaderedEvent doesn't contain room version")
	}
	event := e.Event
	event.roomVersion = e.RoomVersion
	return event
}

// UnwrapEventHeaders unwraps an array of headered events.
func UnwrapEventHeaders(in []HeaderedEvent) []Event {
	result := make([]Event, len(in))
	for i := range in {
		result[i] = in[i].Event
	}
	return result
}

// UnmarshalJSON implements json.Unmarshaller
func (e *HeaderedEvent) UnmarshalJSON(data []byte) error {
	var err error
	// First extract the headers from the JSON.
	var m EventHeader
	if err = json.Unmarshal(data, &m); err != nil {
		return err
	}
	e.EventHeader = m
	// Now strip any of the header fields from the JSON input data.
	fields := reflect.TypeOf(e.EventHeader)
	for i := 0; i < fields.NumField(); i++ {
		tag := strings.Split(fields.Field(i).Tag.Get("json"), ",")[0]
		if data, err = sjson.DeleteBytes(data, tag); err != nil {
			return err
		}
	}
	// Get the event field format.
	eventFormat, err := m.RoomVersion.EventFormat()
	if err != nil {
		return err
	}
	// Check what the room version is and prepare the Event struct for
	// that specific version type.
	switch eventFormat {
	case EventFormatV1:
		e.fields = eventFormatV1Fields{}
	case EventFormatV2:
		e.fields = eventFormatV2Fields{}
	default:
		return UnsupportedRoomVersionError{m.RoomVersion}
	}
	// Finally, unmarshal the remaining event JSON (less the headers)
	// into the event struct.
	if e.Event, err = NewEventFromTrustedJSON(data, false, m.RoomVersion); err != nil {
		return err
	}
	// At this point unmarshalling is complete.
	return nil
}

// MarshalJSON implements json.Marshaller
func (e HeaderedEvent) MarshalJSON() ([]byte, error) {
	var err error
	// First marshal the event struct itself.
	content := e.Event.JSON()
	// Now jump through the fields of the header struct and add them
	// in separately. This is needed because of the way that Go handles
	// function overloading on embedded types, since Event also
	// implements custom marshalling and unmarshalling functions.
	// Doing this also ensures the least number of changes to Event
	// references elsewhere.
	fields := reflect.TypeOf(e.EventHeader)
	values := reflect.ValueOf(e.EventHeader)
	for i := 0; i < fields.NumField(); i++ {
		tag := strings.Split(fields.Field(i).Tag.Get("json"), ",")[0]
		if content, err = sjson.SetBytes(
			content, tag,
			values.Field(i).Interface(),
		); err != nil {
			return []byte{}, err
		}
	}
	// Return the newly marshalled JSON.
	return content, nil
}

type UnexpectedHeaderedEvent struct{}

func (u UnexpectedHeaderedEvent) Error() string {
	return "unexpected headered event"
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"

	"github.com/koffeinsource/go-imgur"
	"github.com/koffeinsource/go-klogger"
)

func printRate(client *imgur.Client) {
	client.Log.Infof("*** RATE LIMIT ***\n")
	rl, err := client.GetRateLimit()
	if err != nil {
		client.Log.Errorf("Error in GetRateLimit: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", *rl)
}

func printImage(client *imgur.Client, image *string) {
	client.Log.Infof("*** IMAGE ***\n")
	img, _, err := client.GetImageInfo(*image)
	if err != nil {
		client.Log.Errorf("Error in GetImageInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printAlbum(client *imgur.Client, album *string) {
	client.Log.Infof("*** ALBUM ***\n")
	img, _, err := client.GetAlbumInfo(*album)
	if err != nil {
		client.Log.Errorf("Error in GetAlbumInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printGImage(client *imgur.Client, gimage *string) {
	client.Log.Infof("*** GALLERY IMAGE ***\n")
	img, _, err := client.GetGalleryImageInfo(*gimage)
	if err != nil {
		client.Log.Errorf("Error in GetGalleryImageInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printGAlbum(client *imgur.Client, galbum *string) {
	client.Log.Infof("*** GALLERY ALBUM ***\n")
	img, _, err := client.GetGalleryAlbumInfo(*galbum)
	if err != nil {
		client.Log.Errorf("Error in GetGalleryAlbumInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printURL(client *imgur.Client, url *string) {
	client.Log.Infof("*** URL ***\n")
	img, _, err := client.GetInfoFromURL(*url)
	if err != nil {
		client.Log.Errorf("Error in GetInfoFromURL: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func main() {
	imgurClientID := flag.String("id", "", "Your imgur client id. REQUIRED!")
	url := flag.String("url", "", "Gets information based on the URL passed.")
	upload := flag.String("upload", "", "Filepath to an image that will be uploaded to imgur.")
	image := flag.String("image", "", "The image ID to be queried.")
	album := flag.String("album", "", "The album ID to be queried.")
	gimage := flag.String("gimage", "", "The gallery image ID to be queried.")
	galbum := flag.String("galbum", "", "The gallery album ID to be queried.")
	rate := flag.Bool("rate", false, "Get the current rate limit.")
	flag.Parse()

	// Check if there is anything todo
	if flag.NFlag() >= 3 || *imgurClientID == "" {
		flag.PrintDefaults()
		return
	}

	client := new(imgur.Client)
	client.HTTPClient = new(http.Client)
	client.Log = new(klogger.CLILogger)
	client.ImgurClientID = *imgurClientID

	if *upload != "" {
		_, st, err := client.UploadImageFromFile(*upload, "", "test title", "test desc")
		if st != 200 || err != nil {
			fmt.Printf("Status: %v\n", st)
			fmt.Printf("Err: %v\n", err)
		}
	}

	if *rate {
		printRate(client)
	}

	if *image != "" {
		printImage(client, image)
	}

	if *album != "" {
		printAlbum(client, album)
	}

	if *gimage != "" {
		printGImage(client, gimage)
	}

	if *galbum != "" {
		printGAlbum(client, galbum)
	}

	if *url != "" {
		printURL(client, url)
	}
}
<commit_msg>imgurcmd: output more useful information on URL query<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"

	"github.com/koffeinsource/go-imgur"
	"github.com/koffeinsource/go-klogger"
)

func printRate(client *imgur.Client) {
	client.Log.Infof("*** RATE LIMIT ***\n")
	rl, err := client.GetRateLimit()
	if err != nil {
		client.Log.Errorf("Error in GetRateLimit: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", *rl)
}

func printImage(client *imgur.Client, image *string) {
	client.Log.Infof("*** IMAGE ***\n")
	img, _, err := client.GetImageInfo(*image)
	if err != nil {
		client.Log.Errorf("Error in GetImageInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printAlbum(client *imgur.Client, album *string) {
	client.Log.Infof("*** ALBUM ***\n")
	img, _, err := client.GetAlbumInfo(*album)
	if err != nil {
		client.Log.Errorf("Error in GetAlbumInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printGImage(client *imgur.Client, gimage *string) {
	client.Log.Infof("*** GALLERY IMAGE ***\n")
	img, _, err := client.GetGalleryImageInfo(*gimage)
	if err != nil {
		client.Log.Errorf("Error in GetGalleryImageInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printGAlbum(client *imgur.Client, galbum *string) {
	client.Log.Infof("*** GALLERY ALBUM ***\n")
	img, _, err := client.GetGalleryAlbumInfo(*galbum)
	if err != nil {
		client.Log.Errorf("Error in GetGalleryAlbumInfo: %v\n", err)
		return
	}
	client.Log.Infof("%v\n", img)
}

func printURL(client *imgur.Client, url *string) {
	client.Log.Infof("*** URL ***\n")
	img, _, err := client.GetInfoFromURL(*url)
	if err != nil {
		client.Log.Errorf("Error in GetInfoFromURL: %v\n", err)
		return
	}
	client.Log.Infof("Image: %+v\n", img.Image)
	client.Log.Infof("Album: %+v\n", img.Album)
	client.Log.Infof("GImage: %+v\n", img.GImage)
	client.Log.Infof("GAlbum: %+v\n", img.GAlbum)
}

func main() {
	imgurClientID := flag.String("id", "", "Your imgur client id. REQUIRED!")
	url := flag.String("url", "", "Gets information based on the URL passed.")
	upload := flag.String("upload", "", "Filepath to an image that will be uploaded to imgur.")
	image := flag.String("image", "", "The image ID to be queried.")
	album := flag.String("album", "", "The album ID to be queried.")
	gimage := flag.String("gimage", "", "The gallery image ID to be queried.")
	galbum := flag.String("galbum", "", "The gallery album ID to be queried.")
	rate := flag.Bool("rate", false, "Get the current rate limit.")
	flag.Parse()

	// Check if there is anything todo
	if flag.NFlag() >= 3 || *imgurClientID == "" {
		flag.PrintDefaults()
		return
	}

	client := new(imgur.Client)
	client.HTTPClient = new(http.Client)
	client.Log = new(klogger.CLILogger)
	client.ImgurClientID = *imgurClientID

	if *upload != "" {
		_, st, err := client.UploadImageFromFile(*upload, "", "test title", "test desc")
		if st != 200 || err != nil {
			fmt.Printf("Status: %v\n", st)
			fmt.Printf("Err: %v\n", err)
		}
	}

	if *rate {
		printRate(client)
	}

	if *image != "" {
		printImage(client, image)
	}

	if *album != "" {
		printAlbum(client, album)
	}

	if *gimage != "" {
		printGImage(client, gimage)
	}

	if *galbum != "" {
		printGAlbum(client, galbum)
	}

	if *url != "" {
		printURL(client, url)
	}
}
<|endoftext|>


<commit_before>// service registration and discovery

package server

import (
	"errors"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"strings"
	"time"
)

const (
	EtcdServersPrefix    = "/pando/servers/"
	EtcdServersPrefixCnt = 2
)

type ServerManager struct {
	serverName string
	// servername -> hosttype -> hostlist
	// eg. var hosts []string = mapServers["testserver"]["rpchost"]
	mapServers map[string](map[string][]string)
	etcdHosts  []string
}

// etcd hosts is config as http://ip1:port1;http://ip2:port2;http://ip3:port3
func NewServerManager(name string, etcd string) (*ServerManager, error) {
	if etcd == "" {
		return nil, errors.New("no etcd host found!")
	}
	return &ServerManager{
		serverName: name,
		etcdHosts:  strings.Split(etcd, ";"),
		mapServers: make(map[string](map[string][]string)),
	}, nil
}

// register server to etcd
func (mgr *ServerManager) RegisterServer() error {
	if serverInstance == nil {
		return errorf(errServerNotInit)
	}
	cfg := client.Config{
		Endpoints: mgr.etcdHosts,
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}
	c, err := client.New(cfg)
	if err != nil {
		return err
	}
	kapi := client.NewKeysAPI(c)
	prefix := EtcdServersPrefix + mgr.serverName + "/"
	var response *client.Response
	opt := &client.SetOptions{TTL: 180 * time.Second}
	if serverInstance.tcpsvr != nil {
		addr, _ := fixHostIp(*confTCPHost)
		response, err = kapi.Set(context.Background(), prefix+FlagTCPHost+"/"+addr, addr, opt)
	}
	if serverInstance.rpcsvr != nil {
		addr, _ := fixHostIp(*confRPCHost)
		response, err = kapi.Set(context.Background(), prefix+FlagRPCHost+"/"+addr, addr, opt)
	}
	if serverInstance.httpsvr != nil {
		addr, _ := fixHostIp(*confHTTPHost)
		response, err = kapi.Set(context.Background(), prefix+FlagHTTPHost+"/"+addr, addr, opt)
	}
	if err != nil {
		return err
	}
	// print common key info
	Log.Infof("RegisterServer is done. Metadata is %q\n", response)

	return nil
}

// update server hosts
func (mgr *ServerManager) UpdateServerHosts() error {
	if serverInstance == nil {
		return errorf(errServerNotInit)
	}

	cfg := client.Config{
		Endpoints: mgr.etcdHosts,
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}
	c, err := client.New(cfg)
	if err != nil {
		return err
	}

	kapi := client.NewKeysAPI(c)
	prefix := EtcdServersPrefix
	opt := &client.GetOptions{Recursive: true}
	response, err := kapi.Get(context.Background(), prefix, opt)
	if err != nil {
		return err
	}

	servers := make(map[string](map[string][]string))

	root := response.Node
	if root.Dir != true {
		return errorf(errWrongEtcdPath, root.Key)
	}
	for _, server := range root.Nodes {
		if server.Dir != true {
			return errorf(errWrongEtcdPath, server.Key)
		}
		name := strings.Split(server.Key, "/")[EtcdServersPrefixCnt+1]
		servers[name] = make(map[string][]string)
		for _, hosttype := range server.Nodes {
			if hosttype.Dir != true {
				return errorf(errWrongEtcdPath, hosttype.Key)
			}
			host := strings.Split(hosttype.Key, "/")[EtcdServersPrefixCnt+2]
			servers[name][host] = []string{}
			for _, hostaddr := range hosttype.Nodes {
				addr := strings.Split(hostaddr.Key, "/")[EtcdServersPrefixCnt+3]
				servers[name][host] = append(servers[name][host], addr)
			}
		}
	}

	mgr.mapServers = servers

	Log.Infof("UpdateServerHosts is done: %v", mgr.mapServers)
	return nil

}

// get host ips for the server, now return all hosts
func (mgr *ServerManager) GetServerHosts(serverName string, hostType string) ([]string, error) {
	server, ok := mgr.mapServers[serverName]
	if !ok {
		// try update server hosts mannually
		mgr.UpdateServerHosts()
	}
	server, ok = mgr.mapServers[serverName]
	if !ok {
		return nil, errorf("no server for %s", serverName)
	}
	hosts, ok := server[hostType]
	if !ok || len(hosts) == 0 {
		return nil, errorf("no hosts for %s:%s", serverName, hostType)
	}

	return hosts, nil
}
<commit_msg>add HTTP_PROXY and TCP_PROXY evironment flag to allow tcp and http proxy or lb frontend.<commit_after>// service registration and discovery

package server

import (
	"errors"
	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
	"os"
	"strings"
	"time"
)

const (
	EtcdServersPrefix    = "/pando/servers/"
	EtcdServersPrefixCnt = 2
	EnvTCPProxy          = "TCP_PROXY"
	EnvHTTPProxy         = "HTTP_PROXY"
)

type ServerManager struct {
	serverName string
	// servername -> hosttype -> hostlist
	// eg. var hosts []string = mapServers["testserver"]["rpchost"]
	mapServers map[string](map[string][]string)
	etcdHosts  []string
}

// etcd hosts is config as http://ip1:port1;http://ip2:port2;http://ip3:port3
func NewServerManager(name string, etcd string) (*ServerManager, error) {
	if etcd == "" {
		return nil, errors.New("no etcd host found!")
	}
	return &ServerManager{
		serverName: name,
		etcdHosts:  strings.Split(etcd, ";"),
		mapServers: make(map[string](map[string][]string)),
	}, nil
}

// register server to etcd
func (mgr *ServerManager) RegisterServer() error {
	if serverInstance == nil {
		return errorf(errServerNotInit)
	}
	cfg := client.Config{
		Endpoints: mgr.etcdHosts,
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}
	c, err := client.New(cfg)
	if err != nil {
		return err
	}
	kapi := client.NewKeysAPI(c)
	prefix := EtcdServersPrefix + mgr.serverName + "/"
	var response *client.Response
	opt := &client.SetOptions{TTL: 90 * time.Second}
	if serverInstance.tcpsvr != nil {
		addr := os.Getenv(EnvTCPProxy)
		if addr == "" {
			addr, _ = fixHostIp(*confTCPHost)
		}
		response, err = kapi.Set(context.Background(), prefix+FlagTCPHost+"/"+addr, addr, opt)
	}
	if serverInstance.rpcsvr != nil {
		addr, _ := fixHostIp(*confRPCHost)
		response, err = kapi.Set(context.Background(), prefix+FlagRPCHost+"/"+addr, addr, opt)
	}
	if serverInstance.httpsvr != nil {
		addr := os.Getenv(EnvHTTPProxy)
		if addr == "" {
			addr, _ = fixHostIp(*confHTTPHost)
		}
		response, err = kapi.Set(context.Background(), prefix+FlagHTTPHost+"/"+addr, addr, opt)
	}
	if err != nil {
		return err
	}
	// print common key info
	Log.Infof("RegisterServer is done. Metadata is %q\n", response)

	return nil
}

// update server hosts
func (mgr *ServerManager) UpdateServerHosts() error {
	if serverInstance == nil {
		return errorf(errServerNotInit)
	}

	cfg := client.Config{
		Endpoints: mgr.etcdHosts,
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}
	c, err := client.New(cfg)
	if err != nil {
		return err
	}

	kapi := client.NewKeysAPI(c)
	prefix := EtcdServersPrefix
	opt := &client.GetOptions{Recursive: true}
	response, err := kapi.Get(context.Background(), prefix, opt)
	if err != nil {
		return err
	}

	servers := make(map[string](map[string][]string))

	root := response.Node
	if root.Dir != true {
		return errorf(errWrongEtcdPath, root.Key)
	}
	for _, server := range root.Nodes {
		if server.Dir != true {
			return errorf(errWrongEtcdPath, server.Key)
		}
		name := strings.Split(server.Key, "/")[EtcdServersPrefixCnt+1]
		servers[name] = make(map[string][]string)
		for _, hosttype := range server.Nodes {
			if hosttype.Dir != true {
				return errorf(errWrongEtcdPath, hosttype.Key)
			}
			host := strings.Split(hosttype.Key, "/")[EtcdServersPrefixCnt+2]
			servers[name][host] = []string{}
			for _, hostaddr := range hosttype.Nodes {
				addr := strings.Split(hostaddr.Key, "/")[EtcdServersPrefixCnt+3]
				servers[name][host] = append(servers[name][host], addr)
			}
		}
	}

	mgr.mapServers = servers

	Log.Infof("UpdateServerHosts is done: %v", mgr.mapServers)
	return nil

}

// get host ips for the server, now return all hosts
func (mgr *ServerManager) GetServerHosts(serverName string, hostType string) ([]string, error) {
	server, ok := mgr.mapServers[serverName]
	if !ok {
		// try update server hosts mannually
		mgr.UpdateServerHosts()
	}
	server, ok = mgr.mapServers[serverName]
	if !ok {
		return nil, errorf("no server for %s", serverName)
	}
	hosts, ok := server[hostType]
	if !ok || len(hosts) == 0 {
		return nil, errorf("no hosts for %s:%s", serverName, hostType)
	}

	return hosts, nil
}
<|endoftext|>


<commit_before>package main

type editor struct {
	basicEditor
	registers
}

func (e *editor) yank(r rune, from, to int) {
	s := e.slice(from, to)
	e.register(r, s)
}

func (e *editor) put(r rune, at int) {
	s := e.read(r)
	e.insert(s, at)
}

func iskeyword(ch rune) bool {
	if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '0' <= ch && ch <= '9' || ch == '_' || 192 <= ch && ch <= 255 {
		return true
	}
	return false
}

func isWhitespace(ch rune) bool {
	if ch == ' ' || ch == '\t' {
		return true
	}
	return false
}

func (e *editor) wordForward() {
	switch n := len(e.buf) - e.pos; {
	case n < 1:
		return
	case n == 1:
		e.pos = len(e.buf)
		return
	}
	switch ch := e.buf[e.pos]; {
	case isWhitespace(ch):
		if i := e.indexFunc(isWhitespace, e.pos+1, false); i > 0 {
			e.pos = i
			return
		}
	case iskeyword(ch):
		if i := e.indexFunc(iskeyword, e.pos+1, false); i > 0 {
			if !isWhitespace(e.buf[i]) {
				e.pos = i
				return
			}
			if i := e.indexFunc(isWhitespace, i+1, false); i > 0 {
				e.pos = i
				return
			}
		}
	default:
		if i := e.indexFunc(func(r rune) bool { return isWhitespace(r) || iskeyword(r) }, e.pos+1, true); i > 0 {
			if iskeyword(e.buf[i]) {
				e.pos = i
				return
			}
			if i := e.indexFunc(isWhitespace, i+1, false); i > 0 {
				e.pos = i
				return
			}
		}
	}
	e.pos = len(e.buf)
}

func (e *editor) wordBackward() {
	switch e.pos {
	case 0:
		return
	case 1:
		e.pos = 0
		return
	}

	n := e.pos - 1
	switch ch := e.buf[n]; {
	case isWhitespace(ch):
		n = e.lastIndexFunc(isWhitespace, n, false)
		if n < 0 {
			e.pos = 0
			return
		}
	}

	switch ch := e.buf[n]; {
	case iskeyword(ch):
		if i := e.lastIndexFunc(iskeyword, n, false); i >= 0 {
			e.pos = i + 1
			return
		}
	default:
		for i := n - 1; i >= 0; i-- {
			switch ch := e.buf[i]; {
			case iskeyword(ch), isWhitespace(ch):
				e.pos = i + 1
				return
			}
		}
	}
	e.pos = 0
}
<commit_msg>Rename iskeyword -> isKeyword<commit_after>package main

type editor struct {
	basicEditor
	registers
}

func (e *editor) yank(r rune, from, to int) {
	s := e.slice(from, to)
	e.register(r, s)
}

func (e *editor) put(r rune, at int) {
	s := e.read(r)
	e.insert(s, at)
}

func isKeyword(ch rune) bool {
	if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '0' <= ch && ch <= '9' || ch == '_' || 192 <= ch && ch <= 255 {
		return true
	}
	return false
}

func isWhitespace(ch rune) bool {
	if ch == ' ' || ch == '\t' {
		return true
	}
	return false
}

func (e *editor) wordForward() {
	switch n := len(e.buf) - e.pos; {
	case n < 1:
		return
	case n == 1:
		e.pos = len(e.buf)
		return
	}
	switch ch := e.buf[e.pos]; {
	case isWhitespace(ch):
		if i := e.indexFunc(isWhitespace, e.pos+1, false); i > 0 {
			e.pos = i
			return
		}
	case isKeyword(ch):
		if i := e.indexFunc(isKeyword, e.pos+1, false); i > 0 {
			if !isWhitespace(e.buf[i]) {
				e.pos = i
				return
			}
			if i := e.indexFunc(isWhitespace, i+1, false); i > 0 {
				e.pos = i
				return
			}
		}
	default:
		if i := e.indexFunc(func(r rune) bool { return isWhitespace(r) || isKeyword(r) }, e.pos+1, true); i > 0 {
			if isKeyword(e.buf[i]) {
				e.pos = i
				return
			}
			if i := e.indexFunc(isWhitespace, i+1, false); i > 0 {
				e.pos = i
				return
			}
		}
	}
	e.pos = len(e.buf)
}

func (e *editor) wordBackward() {
	switch e.pos {
	case 0:
		return
	case 1:
		e.pos = 0
		return
	}

	n := e.pos - 1
	switch ch := e.buf[n]; {
	case isWhitespace(ch):
		n = e.lastIndexFunc(isWhitespace, n, false)
		if n < 0 {
			e.pos = 0
			return
		}
	}

	switch ch := e.buf[n]; {
	case isKeyword(ch):
		if i := e.lastIndexFunc(isKeyword, n, false); i >= 0 {
			e.pos = i + 1
			return
		}
	default:
		for i := n - 1; i >= 0; i-- {
			switch ch := e.buf[i]; {
			case isKeyword(ch), isWhitespace(ch):
				e.pos = i + 1
				return
			}
		}
	}
	e.pos = 0
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gocql/gocql"
	"github.com/skeswa/gophr/common"
	"github.com/skeswa/gophr/common/config"
	"github.com/skeswa/gophr/common/depot"
	"github.com/skeswa/gophr/common/github"
	"github.com/skeswa/gophr/common/io"
	"github.com/skeswa/gophr/common/models"
	"github.com/skeswa/gophr/common/verdeps"
)

const (
	formKeyGoGet                = "go-get"
	formValueGoGet              = "1"
	contentTypeHTML             = "text/html"
	someFakeGitTagRef           = "refs/tags/thisisnotathinginanyrepowehopenothatitmatters"
	gitRefsInfoSubPath          = "/info/refs"
	httpLocationHeader          = "Location"
	gitUploadPackSubPath        = "/git-upload-pack"
	httpContentTypeHeader       = "Content-Type"
	packagePageURLTemplate      = "https://%s/#/packages/%s/%s"
	contentTypeGitUploadPack    = "application/x-git-upload-pack-advertisement"
	githubUploadPackURLTemplate = "https://github.com/%s/%s/git-upload-pack"
)

// TODO(skeswa): IMPORTANT! When we merge in depot, go-gets will no longer
// require ref fetches for some go-gets.

// PackageRequest is stuct that standardizes the output of all the scenarios
// through which a package may be requested. PackageRequest is essentially a
// helper struct to move data between the sub-functions of
// RespondToPackageRequest and RespondToPackageRequest itself.
type packageRequest struct {
	req             *http.Request
	parts           *packageRequestParts
	refsData        []byte
	matchedSHA      string
	matchedSHALabel string
}

// newPackageRequestArgs is the arguments struct for newPackageRequest.
type newPackageRequestArgs struct {
	req          *http.Request
	downloadRefs refsDownloader
}

// newPackageRequest parses and simplifies the information in a package version
// request in order to make serializing a response easier.
func newPackageRequest(args newPackageRequestArgs) (*packageRequest, error) {
	// Read the parts of the package request.
	parts, err := readPackageRequestParts(args.req)
	if err != nil {
		return nil, err
	}

	var (
		refs            common.Refs
		matchedSHA      string
		matchedSHALabel string
		packageRefsData []byte
	)

	// Only go out to fetch refs if they're going to get used.
	if isGoGetRequest(args.req) || isInfoRefsRequest(parts) {
		// Get and process all of the refs for this package.
		if refs, err = args.downloadRefs(
			parts.author,
			parts.repo); err != nil {
			return nil, err
		}

		// Set the default matched sha.
		matchedSHA = refs.MasterRefHash

		// Figure out what the best candidate is.
		if parts.hasSemverSelector() {
			// If there are no candidates, return in failure.
			if refs.Candidates == nil || len(refs.Candidates) < 1 {
				return nil, NewNoSuchPackageVersionError(
					parts.author,
					parts.repo,
					parts.semverSelector.String())
			}

			// Find the best candidate.
			bestCandidate := refs.Candidates.Best(parts.semverSelector)
			if bestCandidate == nil {
				return nil, NewNoSuchPackageVersionError(
					parts.author,
					parts.repo,
					parts.semverSelector.String())
			}

			// Re-serialize the refs data with said candidate.
			matchedSHA = bestCandidate.GitRefHash
			matchedSHALabel = bestCandidate.String()
			packageRefsData = refs.Reserialize(
				bestCandidate.GitRefName,
				bestCandidate.GitRefHash)
		} else if parts.hasSHASelector() {
			// Re-serialize the refs data with the sha.
			matchedSHA = parts.shaSelector
			packageRefsData = refs.Reserialize(
				someFakeGitTagRef,
				parts.shaSelector)
			// TODO(skeswa): investigate validating the ref to see if it actually
			// exists.
		} else {
			// Since there was no selector, we are fine with the fact that we didn't
			// find a match. Now, return the original refs that we downloaded from
			// github that point to master by default.
			packageRefsData = refs.Data
		}
	}

	return &packageRequest{
		req:             args.req,
		parts:           parts,
		refsData:        packageRefsData,
		matchedSHA:      matchedSHA,
		matchedSHALabel: matchedSHALabel,
	}, nil
}

// respondToPackageRequestArgs is the arguments struct for
// packageRequest#respond.
type respondToPackageRequestArgs struct {
	io                    io.IO
	db                    *gocql.Session
	res                   http.ResponseWriter
	conf                  *config.Config
	creds                 *config.Credentials
	ghSvc                 github.RequestService
	versionPackage        packageVersioner
	isPackageArchived     packageArchivalChecker
	recordPackageArchival packageArchivalRecorder
	recordPackageDownload packageDownloadRecorder
}

// respond crafts an appropriate response for a package request, serializes the
// aforesaid response and sends it back to the original client.
func (pr *packageRequest) respond(args respondToPackageRequestArgs) error {
	// Take care of the cases that deoend inf variations in the subpath.
	if pr.parts.subpath == gitUploadPackSubPath ||
		pr.parts.subpath == gitRefsInfoSubPath {
		// Check whether this package has already been archived.
		packageArchived, err := args.isPackageArchived(packageArchivalCheckerArgs{
			db:                    args.db,
			sha:                   pr.matchedSHA,
			repo:                  pr.parts.repo,
			author:                pr.parts.author,
			packageExistsInDepot:  depot.RepoExists,
			recordPackageArchival: args.recordPackageArchival,
			isPackageArchivedInDB: models.IsPackageArchived,
		})
		// If we cannot check whether a package has been archived, return
		// unsuccessfully.
		if err != nil {
			return err
		}

		// Only run the sub-versioning for this package if we haven't before.
		if !packageArchived {
			// Indicate in the logs that the package was archived.
			log.Printf(
				"Package %s/%s@%s has not yet been archived.\n",
				pr.parts.author,
				pr.parts.repo,
				pr.matchedSHA)

			// Perform sub-versioning.
			if err := args.versionPackage(packageVersionerArgs{
				io:                     args.io,
				db:                     args.db,
				sha:                    pr.matchedSHA,
				repo:                   pr.parts.repo,
				conf:                   args.conf,
				creds:                  args.creds,
				ghSvc:                  args.ghSvc,
				author:                 pr.parts.author,
				pushToDepot:            pushToDepot,
				versionDeps:            verdeps.VersionDeps,
				downloadPackage:        downloadPackage,
				createDepotRepo:        depot.CreateNewRepo,
				destroyDepotRepo:       depot.DestroyRepo,
				isPackageArchived:      isPackageArchived,
				constructionZonePath:   args.conf.ConstructionZonePath,
				recordPackageArchival:  args.recordPackageArchival,
				attemptWorkDirDeletion: deleteFolder,
			}); err != nil {
				// Report the sub-versioning failure to the logs.
				log.Printf(
					"Sub-versioning failed for package %s/%s@%s: %v\n",
					pr.parts.author,
					pr.parts.repo,
					pr.matchedSHA,
					err)

				return err
			}
		}

		// Send a 301 stipulating the repository can be found on depot.
		args.res.Header().Set(
			httpLocationHeader,
			depot.BuildExternalRepoURL(
				getRequestDomain(pr.req),
				pr.parts.author,
				pr.parts.repo,
				pr.matchedSHA))
		args.res.WriteHeader(http.StatusMovedPermanently)
		return nil
	}

	// This means that go-get is requesting package/repository metadata.
	if isGoGetRequest(pr.req) {
		// Without blocking, count go-get surveying this package for installation
		// as a download in the database.
		go args.recordPackageDownload(packageDownloadRecorderArgs{
			db:     args.db,
			sha:    pr.matchedSHA,
			repo:   pr.parts.repo,
			author: pr.parts.author,
			// It is ok for the matched sha label to be left blank.
			version: pr.matchedSHALabel,
		})

		// Compile the go-get metadata accordingly.
		var (
			domain   = getRequestDomain(pr.req)
			metaData = []byte(generateGoGetMetadata(generateGoGetMetadataArgs{
				gophrURL:        domain + pr.req.URL.Path,
				treeURLTemplate: generateGithubTreeURLTemplate(pr.parts.author, pr.parts.repo, pr.matchedSHA),
				blobURLTemplate: generateDepotBlobURLTemplate(domain, pr.parts.author, pr.parts.repo, pr.matchedSHA),
			}))
		)

		// Return the go-get metadata.
		args.res.Header().Set(httpContentTypeHeader, contentTypeHTML)
		args.res.Write(metaData)
		return nil
	}

	// If none of the other cases matched, then redirect to the package page.
	// TODO(skeswa): make this redirect specific to the version of the package.
	args.res.Header().Set(
		httpLocationHeader,
		fmt.Sprintf(
			packagePageURLTemplate,
			pr.req.URL.Host,
			pr.parts.author,
			pr.parts.repo))
	args.res.WriteHeader(http.StatusMovedPermanently)
	return nil
}

// isGoGetRequest returns true if the request was made by go get.
func isGoGetRequest(req *http.Request) bool {
	return req.FormValue(formKeyGoGet) == formValueGoGet
}

// isInfoRefsRequest returns true if the request parts reflect that the request
// is a git refs info request.
func isInfoRefsRequest(parts *packageRequestParts) bool {
	return parts.subpath == gitRefsInfoSubPath
}

// getRequestDomain isolates and returns the domain of the specified request.
func getRequestDomain(req *http.Request) string {
	// If there is no request, don't make a fuss: just return empty.
	if req == nil {
		return ""
	}

	// Resolve the domain of the request.
	var domain string
	if len(req.Host) > 0 {
		domain = req.Host
	} else if len(req.URL.Host) > 0 {
		domain = req.URL.Host
	} else {
		// This is a last resort.
		domain = "gophr.pm"
	}

	return domain
}
<commit_msg>modifying package request to be up to spec<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gocql/gocql"
	"github.com/skeswa/gophr/common"
	"github.com/skeswa/gophr/common/config"
	"github.com/skeswa/gophr/common/depot"
	"github.com/skeswa/gophr/common/github"
	"github.com/skeswa/gophr/common/io"
	"github.com/skeswa/gophr/common/models"
	"github.com/skeswa/gophr/common/verdeps"
)

const (
	formKeyGoGet           = "go-get"
	formValueGoGet         = "1"
	contentTypeHTML        = "text/html"
	someFakeGitTagRef      = "refs/tags/thisisnotathinginanyrepowehopenothatitmatters"
	gitRefsInfoSubPath     = "/info/refs"
	httpLocationHeader     = "Location"
	gitUploadPackSubPath   = "/git-upload-pack"
	httpContentTypeHeader  = "Content-Type"
	packagePageURLTemplate = "https://%s/#/packages/%s/%s"
)

// TODO(skeswa): IMPORTANT! When we merge in depot, go-gets will no longer
// require ref fetches for some go-gets.

// PackageRequest is stuct that standardizes the output of all the scenarios
// through which a package may be requested. PackageRequest is essentially a
// helper struct to move data between the sub-functions of
// RespondToPackageRequest and RespondToPackageRequest itself.
type packageRequest struct {
	req             *http.Request
	parts           *packageRequestParts
	refsData        []byte
	matchedSHA      string
	matchedSHALabel string
}

// newPackageRequestArgs is the arguments struct for newPackageRequest.
type newPackageRequestArgs struct {
	req          *http.Request
	downloadRefs refsDownloader
}

// newPackageRequest parses and simplifies the information in a package version
// request in order to make serializing a response easier.
func newPackageRequest(args newPackageRequestArgs) (*packageRequest, error) {
	// Read the parts of the package request.
	parts, err := readPackageRequestParts(args.req)
	if err != nil {
		return nil, err
	}

	var (
		refs            common.Refs
		matchedSHA      string
		matchedSHALabel string
		packageRefsData []byte
	)

	// Only go out to github to get the matched SHA if the matched SHA is
	// necessary.
	if isGoGetRequest(args.req) || isGitRequest(parts) {
		// Get and process all of the refs for this package.
		if refs, err = args.downloadRefs(
			parts.author,
			parts.repo); err != nil {
			return nil, err
		}

		// Set the default matched sha.
		matchedSHA = refs.MasterRefHash

		// Figure out what the best candidate is.
		if parts.hasSemverSelector() {
			// If there are no candidates, return in failure.
			if refs.Candidates == nil || len(refs.Candidates) < 1 {
				return nil, NewNoSuchPackageVersionError(
					parts.author,
					parts.repo,
					parts.semverSelector.String())
			}

			// Find the best candidate.
			bestCandidate := refs.Candidates.Best(parts.semverSelector)
			if bestCandidate == nil {
				return nil, NewNoSuchPackageVersionError(
					parts.author,
					parts.repo,
					parts.semverSelector.String())
			}

			// Re-serialize the refs data with said candidate.
			matchedSHA = bestCandidate.GitRefHash
			matchedSHALabel = bestCandidate.String()
			packageRefsData = refs.Reserialize(
				bestCandidate.GitRefName,
				bestCandidate.GitRefHash)
		} else if parts.hasSHASelector() {
			// Re-serialize the refs data with the sha.
			matchedSHA = parts.shaSelector
			packageRefsData = refs.Reserialize(
				someFakeGitTagRef,
				parts.shaSelector)
			// TODO(skeswa): investigate validating the ref to see if it actually
			// exists.
		} else {
			// Since there was no selector, we are fine with the fact that we didn't
			// find a match. Now, return the original refs that we downloaded from
			// github that point to master by default.
			packageRefsData = refs.Data
		}
	}

	return &packageRequest{
		req:             args.req,
		parts:           parts,
		refsData:        packageRefsData,
		matchedSHA:      matchedSHA,
		matchedSHALabel: matchedSHALabel,
	}, nil
}

// respondToPackageRequestArgs is the arguments struct for
// packageRequest#respond.
type respondToPackageRequestArgs struct {
	io                    io.IO
	db                    *gocql.Session
	res                   http.ResponseWriter
	conf                  *config.Config
	creds                 *config.Credentials
	ghSvc                 github.RequestService
	versionPackage        packageVersioner
	isPackageArchived     packageArchivalChecker
	recordPackageArchival packageArchivalRecorder
	recordPackageDownload packageDownloadRecorder
}

// respond crafts an appropriate response for a package request, serializes the
// aforesaid response and sends it back to the original client.
func (pr *packageRequest) respond(args respondToPackageRequestArgs) error {
	// If this is a git request, make sure that the depot repo at exists.
	if isGitRequest(pr.parts) {
		// Check whether this package has already been archived.
		packageArchived, err := args.isPackageArchived(packageArchivalCheckerArgs{
			db:                    args.db,
			sha:                   pr.matchedSHA,
			repo:                  pr.parts.repo,
			author:                pr.parts.author,
			packageExistsInDepot:  depot.RepoExists,
			recordPackageArchival: args.recordPackageArchival,
			isPackageArchivedInDB: models.IsPackageArchived,
		})
		// If we cannot check whether a package has been archived, return
		// unsuccessfully.
		if err != nil {
			return err
		}

		// Only run the sub-versioning for this package if we haven't before.
		if !packageArchived {
			// Indicate in the logs that the package was archived.
			log.Printf(
				"Package %s/%s@%s has not yet been archived.\n",
				pr.parts.author,
				pr.parts.repo,
				pr.matchedSHA)

			// Perform sub-versioning.
			if err := args.versionPackage(packageVersionerArgs{
				io:                     args.io,
				db:                     args.db,
				sha:                    pr.matchedSHA,
				repo:                   pr.parts.repo,
				conf:                   args.conf,
				creds:                  args.creds,
				ghSvc:                  args.ghSvc,
				author:                 pr.parts.author,
				pushToDepot:            pushToDepot,
				versionDeps:            verdeps.VersionDeps,
				downloadPackage:        downloadPackage,
				createDepotRepo:        depot.CreateNewRepo,
				destroyDepotRepo:       depot.DestroyRepo,
				isPackageArchived:      isPackageArchived,
				constructionZonePath:   args.conf.ConstructionZonePath,
				recordPackageArchival:  args.recordPackageArchival,
				attemptWorkDirDeletion: deleteFolder,
			}); err != nil {
				// Report the sub-versioning failure to the logs.
				log.Printf(
					"Sub-versioning failed for package %s/%s@%s: %v\n",
					pr.parts.author,
					pr.parts.repo,
					pr.matchedSHA,
					err)

				return err
			}
		}

		// All git requests are going to require a redirect, so build the base
		// repo URL for a redirect.
		depotRepoURL := depot.BuildExternalRepoURL(
			getRequestDomain(pr.req),
			pr.parts.author,
			pr.parts.repo,
			pr.matchedSHA)

		// Set the redirect location.
		args.res.Header().Set(
			httpLocationHeader,
			depotRepoURL+pr.parts.subpath)
		// Write status code 301.
		resp.WriteHeader(http.StatusMovedPermanently)
	}

	// This means that go-get is requesting package/repository metadata.
	if isGoGetRequest(pr.req) {
		// Without blocking, count go-get surveying this package for installation
		// as a download in the database.
		go args.recordPackageDownload(packageDownloadRecorderArgs{
			db:     args.db,
			sha:    pr.matchedSHA,
			repo:   pr.parts.repo,
			author: pr.parts.author,
			// It is ok for the matched sha label to be left blank.
			version: pr.matchedSHALabel,
		})

		// Compile the go-get metadata accordingly.
		var (
			domain   = getRequestDomain(pr.req)
			metaData = []byte(generateGoGetMetadata(generateGoGetMetadataArgs{
				gophrURL: domain + pr.req.URL.Path,
				treeURLTemplate: generateGithubTreeURLTemplate(
					pr.parts.author,
					pr.parts.repo,
					pr.matchedSHA),
				blobURLTemplate: generateDepotBlobURLTemplate(
					domain,
					pr.parts.author,
					pr.parts.repo,
					pr.matchedSHA),
			}))
		)

		// Return the go-get metadata.
		args.res.Header().Set(httpContentTypeHeader, contentTypeHTML)
		args.res.Write(metaData)
		return nil
	}

	// If none of the other cases matched, then redirect to the package page.
	// TODO(skeswa): make this redirect specific to the version of the package.
	args.res.Header().Set(
		httpLocationHeader,
		fmt.Sprintf(
			packagePageURLTemplate,
			pr.req.URL.Host,
			pr.parts.author,
			pr.parts.repo))
	args.res.WriteHeader(http.StatusMovedPermanently)
	return nil
}

// isGoGetRequest returns true if the request was made by go get.
func isGoGetRequest(req *http.Request) bool {
	return req.FormValue(formKeyGoGet) == formValueGoGet
}

// isGitRequest returns true if the request parts reflect that the request
// is made by, or for, git.
func isGitRequest(parts *packageRequestParts) bool {
	return parts.subpath == gitRefsInfoSubPath ||
		parts.subpath == gitUploadPackSubPath
}

// getRequestDomain isolates and returns the domain of the specified request.
func getRequestDomain(req *http.Request) string {
	// If there is no request, don't make a fuss: just return empty.
	if req == nil {
		return ""
	}

	// Resolve the domain of the request.
	var domain string
	if len(req.Host) > 0 {
		domain = req.Host
	} else if len(req.URL.Host) > 0 {
		domain = req.URL.Host
	} else {
		// This is a last resort.
		domain = "gophr.pm"
	}

	return domain
}
<|endoftext|>