<commit_before>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/form"
)

// SubscriptionStatus is the list of allowed values for the subscription's status.
type SubscriptionStatus string

// List of values that SubscriptionStatus can take.
const (
	SubscriptionStatusActive   SubscriptionStatus = "active"
	SubscriptionStatusAll      SubscriptionStatus = "all"
	SubscriptionStatusCanceled SubscriptionStatus = "canceled"
	SubscriptionStatusPastDue  SubscriptionStatus = "past_due"
	SubscriptionStatusTrialing SubscriptionStatus = "trialing"
	SubscriptionStatusUnpaid   SubscriptionStatus = "unpaid"
)

// SubscriptionBilling is the type of billing method for this subscription's invoices.
type SubscriptionBilling string

// List of values that SubscriptionBilling can take.
const (
	SubscriptionBillingChargeAutomatically SubscriptionBilling = "charge_automatically"
	SubscriptionBillingSendInvoice         SubscriptionBilling = "send_invoice"
)

// SubscriptionTransferDataParams is the set of parameters allowed for the transfer_data hash.
type SubscriptionTransferDataParams struct {
	Destination *string `form:"destination"`
}

// SubscriptionParams is the set of parameters that can be used when creating or updating a subscription.
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubscriptionParams struct {
	Params                      `form:"*"`
	ApplicationFeePercent       *float64                             `form:"application_fee_percent"`
	Billing                     *string                              `form:"billing"`
	BillingCycleAnchor          *int64                               `form:"billing_cycle_anchor"`
	BillingCycleAnchorNow       *bool                                `form:"-"` // See custom AppendTo
	BillingCycleAnchorUnchanged *bool                                `form:"-"` // See custom AppendTo
	BillingThresholds           *SubscriptionBillingThresholdsParams `form:"billing_thresholds"`
	CancelAtPeriodEnd           *bool                                `form:"cancel_at_period_end"`
	Card                        *CardParams                          `form:"card"`
	Coupon                      *string                              `form:"coupon"`
	Customer                    *string                              `form:"customer"`
	DaysUntilDue                *int64                               `form:"days_until_due"`
	DefaultSource               *string                              `form:"default_source"`
	Items                       []*SubscriptionItemsParams           `form:"items"`
	OnBehalfOf                  *string                              `form:"on_behalf_of"`
	Plan                        *string                              `form:"plan"`
	Prorate                     *bool                                `form:"prorate"`
	ProrationDate               *int64                               `form:"proration_date"`
	Quantity                    *int64                               `form:"quantity"`
	TaxPercent                  *float64                             `form:"tax_percent"`
	TrialEnd                    *int64                               `form:"trial_end"`
	TransferData                *SubscriptionTransferDataParams      `form:"transfer_data"`
	TrialEndNow                 *bool                                `form:"-"` // See custom AppendTo
	TrialFromPlan               *bool                                `form:"trial_from_plan"`
	TrialPeriodDays             *int64                               `form:"trial_period_days"`
}

// SubscriptionBillingThresholdsParams is a structure representing the parameters allowed to control
// billing thresholds for a subscription.
type SubscriptionBillingThresholdsParams struct {
	AmountGTE               *int64 `form:"amount_gte"`
	ResetBillingCycleAnchor *bool  `form:"reset_billing_cycle_anchor"`
}

// SubscriptionCancelParams is the set of parameters that can be used when canceling a subscription.
// For more details see https://stripe.com/docs/api#cancel_subscription
type SubscriptionCancelParams struct {
	Params     `form:"*"`
	InvoiceNow *bool `form:"invoice_now"`
	Prorate    *bool `form:"prorate"`
}

// AppendTo implements custom encoding logic for SubscriptionParams so that the special
// "now" value for billing_cycle_anchor and trial_end can be implemented
// (they're otherwise timestamps rather than strings).
func (p *SubscriptionParams) AppendTo(body *form.Values, keyParts []string) {
	if BoolValue(p.BillingCycleAnchorNow) {
		body.Add(form.FormatKey(append(keyParts, "billing_cycle_anchor")), "now")
	}

	if BoolValue(p.BillingCycleAnchorUnchanged) {
		body.Add(form.FormatKey(append(keyParts, "billing_cycle_anchor")), "unchanged")
	}

	if BoolValue(p.TrialEndNow) {
		body.Add(form.FormatKey(append(keyParts, "trial_end")), "now")
	}
}

// SubscriptionItemsParams is the set of parameters that can be used when creating or updating a subscription item on a subscription
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubscriptionItemsParams struct {
	Params     `form:"*"`
	ClearUsage *bool   `form:"clear_usage"`
	Deleted    *bool   `form:"deleted"`
	ID         *string `form:"id"`
	Plan       *string `form:"plan"`
	Quantity   *int64  `form:"quantity"`
}

// SubscriptionListParams is the set of parameters that can be used when listing active subscriptions.
// For more details see https://stripe.com/docs/api#list_subscriptions.
type SubscriptionListParams struct {
	ListParams   `form:"*"`
	Billing      string            `form:"billing"`
	Created      int64             `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Customer     string            `form:"customer"`
	Plan         string            `form:"plan"`
	Status       string            `form:"status"`
}

// SubscriptionTransferData represents the information for the transfer_data associated with a subscription.
type SubscriptionTransferData struct {
	Destination *Account `json:"destination"`
}

// Subscription is the resource representing a Stripe subscription.
// For more details see https://stripe.com/docs/api#subscriptions.
type Subscription struct {
	ApplicationFeePercent float64                        `json:"application_fee_percent"`
	Billing               SubscriptionBilling            `json:"billing"`
	BillingCycleAnchor    int64                          `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds `json:"billing_thresholds"`
	CanceledAt            int64                          `json:"canceled_at"`
	Created               int64                          `json:"created"`
	CurrentPeriodEnd      int64                          `json:"current_period_end"`
	CurrentPeriodStart    int64                          `json:"current_period_start"`
	Customer              *Customer                      `json:"customer"`
	DaysUntilDue          int64                          `json:"days_until_due"`
	DefaultSource         *PaymentSource                 `json:"default_source"`
	Discount              *Discount                      `json:"discount"`
	CancelAtPeriodEnd     bool                           `json:"cancel_at_period_end"`
	EndedAt               int64                          `json:"ended_at"`
	ID                    string                         `json:"id"`
	Items                 *SubscriptionItemList          `json:"items"`
	Livemode              bool                           `json:"livemode"`
	Metadata              map[string]string              `json:"metadata"`
	Object                string                         `json:"object"`
	OnBehalfOf            *Account                       `json:"on_behalf_of"`
	Plan                  *Plan                          `json:"plan"`
	Quantity              int64                          `json:"quantity"`
	Start                 int64                          `json:"start"`
	Status                SubscriptionStatus             `json:"status"`
	TaxPercent            float64                        `json:"tax_percent"`
	TransferData          *SubscriptionTransferData      `json:"transfer_data"`
	TrialEnd              int64                          `json:"trial_end"`
	TrialStart            int64                          `json:"trial_start"`
}

// SubscriptionBillingThresholds is a structure representing the billing thresholds for a subscription.
type SubscriptionBillingThresholds struct {
	AmountGTE               int64 `json:"amount_gte"`
	ResetBillingCycleAnchor bool  `json:"reset_billing_cycle_anchor"`
}

// SubscriptionList is a list object for subscriptions.
type SubscriptionList struct {
	ListMeta
	Data []*Subscription `json:"data"`
}

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

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

	*s = Subscription(v)
	return nil
}
<commit_msg>Add support for `latest_invoice` on `Subscription`<commit_after>package stripe

import (
	"encoding/json"

	"github.com/stripe/stripe-go/form"
)

// SubscriptionStatus is the list of allowed values for the subscription's status.
type SubscriptionStatus string

// List of values that SubscriptionStatus can take.
const (
	SubscriptionStatusActive   SubscriptionStatus = "active"
	SubscriptionStatusAll      SubscriptionStatus = "all"
	SubscriptionStatusCanceled SubscriptionStatus = "canceled"
	SubscriptionStatusPastDue  SubscriptionStatus = "past_due"
	SubscriptionStatusTrialing SubscriptionStatus = "trialing"
	SubscriptionStatusUnpaid   SubscriptionStatus = "unpaid"
)

// SubscriptionBilling is the type of billing method for this subscription's invoices.
type SubscriptionBilling string

// List of values that SubscriptionBilling can take.
const (
	SubscriptionBillingChargeAutomatically SubscriptionBilling = "charge_automatically"
	SubscriptionBillingSendInvoice         SubscriptionBilling = "send_invoice"
)

// SubscriptionTransferDataParams is the set of parameters allowed for the transfer_data hash.
type SubscriptionTransferDataParams struct {
	Destination *string `form:"destination"`
}

// SubscriptionParams is the set of parameters that can be used when creating or updating a subscription.
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubscriptionParams struct {
	Params                      `form:"*"`
	ApplicationFeePercent       *float64                             `form:"application_fee_percent"`
	Billing                     *string                              `form:"billing"`
	BillingCycleAnchor          *int64                               `form:"billing_cycle_anchor"`
	BillingCycleAnchorNow       *bool                                `form:"-"` // See custom AppendTo
	BillingCycleAnchorUnchanged *bool                                `form:"-"` // See custom AppendTo
	BillingThresholds           *SubscriptionBillingThresholdsParams `form:"billing_thresholds"`
	CancelAtPeriodEnd           *bool                                `form:"cancel_at_period_end"`
	Card                        *CardParams                          `form:"card"`
	Coupon                      *string                              `form:"coupon"`
	Customer                    *string                              `form:"customer"`
	DaysUntilDue                *int64                               `form:"days_until_due"`
	DefaultSource               *string                              `form:"default_source"`
	Items                       []*SubscriptionItemsParams           `form:"items"`
	OnBehalfOf                  *string                              `form:"on_behalf_of"`
	Plan                        *string                              `form:"plan"`
	Prorate                     *bool                                `form:"prorate"`
	ProrationDate               *int64                               `form:"proration_date"`
	Quantity                    *int64                               `form:"quantity"`
	TaxPercent                  *float64                             `form:"tax_percent"`
	TrialEnd                    *int64                               `form:"trial_end"`
	TransferData                *SubscriptionTransferDataParams      `form:"transfer_data"`
	TrialEndNow                 *bool                                `form:"-"` // See custom AppendTo
	TrialFromPlan               *bool                                `form:"trial_from_plan"`
	TrialPeriodDays             *int64                               `form:"trial_period_days"`
}

// SubscriptionBillingThresholdsParams is a structure representing the parameters allowed to control
// billing thresholds for a subscription.
type SubscriptionBillingThresholdsParams struct {
	AmountGTE               *int64 `form:"amount_gte"`
	ResetBillingCycleAnchor *bool  `form:"reset_billing_cycle_anchor"`
}

// SubscriptionCancelParams is the set of parameters that can be used when canceling a subscription.
// For more details see https://stripe.com/docs/api#cancel_subscription
type SubscriptionCancelParams struct {
	Params     `form:"*"`
	InvoiceNow *bool `form:"invoice_now"`
	Prorate    *bool `form:"prorate"`
}

// AppendTo implements custom encoding logic for SubscriptionParams so that the special
// "now" value for billing_cycle_anchor and trial_end can be implemented
// (they're otherwise timestamps rather than strings).
func (p *SubscriptionParams) AppendTo(body *form.Values, keyParts []string) {
	if BoolValue(p.BillingCycleAnchorNow) {
		body.Add(form.FormatKey(append(keyParts, "billing_cycle_anchor")), "now")
	}

	if BoolValue(p.BillingCycleAnchorUnchanged) {
		body.Add(form.FormatKey(append(keyParts, "billing_cycle_anchor")), "unchanged")
	}

	if BoolValue(p.TrialEndNow) {
		body.Add(form.FormatKey(append(keyParts, "trial_end")), "now")
	}
}

// SubscriptionItemsParams is the set of parameters that can be used when creating or updating a subscription item on a subscription
// For more details see https://stripe.com/docs/api#create_subscription and https://stripe.com/docs/api#update_subscription.
type SubscriptionItemsParams struct {
	Params     `form:"*"`
	ClearUsage *bool   `form:"clear_usage"`
	Deleted    *bool   `form:"deleted"`
	ID         *string `form:"id"`
	Plan       *string `form:"plan"`
	Quantity   *int64  `form:"quantity"`
}

// SubscriptionListParams is the set of parameters that can be used when listing active subscriptions.
// For more details see https://stripe.com/docs/api#list_subscriptions.
type SubscriptionListParams struct {
	ListParams   `form:"*"`
	Billing      string            `form:"billing"`
	Created      int64             `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Customer     string            `form:"customer"`
	Plan         string            `form:"plan"`
	Status       string            `form:"status"`
}

// SubscriptionTransferData represents the information for the transfer_data associated with a subscription.
type SubscriptionTransferData struct {
	Destination *Account `json:"destination"`
}

// Subscription is the resource representing a Stripe subscription.
// For more details see https://stripe.com/docs/api#subscriptions.
type Subscription struct {
	ApplicationFeePercent float64                        `json:"application_fee_percent"`
	Billing               SubscriptionBilling            `json:"billing"`
	BillingCycleAnchor    int64                          `json:"billing_cycle_anchor"`
	BillingThresholds     *SubscriptionBillingThresholds `json:"billing_thresholds"`
	CanceledAt            int64                          `json:"canceled_at"`
	Created               int64                          `json:"created"`
	CurrentPeriodEnd      int64                          `json:"current_period_end"`
	CurrentPeriodStart    int64                          `json:"current_period_start"`
	Customer              *Customer                      `json:"customer"`
	DaysUntilDue          int64                          `json:"days_until_due"`
	DefaultSource         *PaymentSource                 `json:"default_source"`
	Discount              *Discount                      `json:"discount"`
	CancelAtPeriodEnd     bool                           `json:"cancel_at_period_end"`
	EndedAt               int64                          `json:"ended_at"`
	ID                    string                         `json:"id"`
	Items                 *SubscriptionItemList          `json:"items"`
	LatestInvoice         *Invoice                       `json:"latest_invoice"`
	Livemode              bool                           `json:"livemode"`
	Metadata              map[string]string              `json:"metadata"`
	Object                string                         `json:"object"`
	OnBehalfOf            *Account                       `json:"on_behalf_of"`
	Plan                  *Plan                          `json:"plan"`
	Quantity              int64                          `json:"quantity"`
	Start                 int64                          `json:"start"`
	Status                SubscriptionStatus             `json:"status"`
	TaxPercent            float64                        `json:"tax_percent"`
	TransferData          *SubscriptionTransferData      `json:"transfer_data"`
	TrialEnd              int64                          `json:"trial_end"`
	TrialStart            int64                          `json:"trial_start"`
}

// SubscriptionBillingThresholds is a structure representing the billing thresholds for a subscription.
type SubscriptionBillingThresholds struct {
	AmountGTE               int64 `json:"amount_gte"`
	ResetBillingCycleAnchor bool  `json:"reset_billing_cycle_anchor"`
}

// SubscriptionList is a list object for subscriptions.
type SubscriptionList struct {
	ListMeta
	Data []*Subscription `json:"data"`
}

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

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

	*s = Subscription(v)
	return nil
}
<|endoftext|>


<commit_before>package auth

import (
	"fmt"
	"net/http"
)

func IsAdmin(r *http.Request) bool {
	isAdmin, present := r.Context().Value(isAdminKey).(bool)
	fmt.Printf("isAdmin %t and preset %t", isAdmin, present)
	return present && isAdmin
}
<commit_msg>kill debug logging<commit_after>package auth

import "net/http"

func IsAdmin(r *http.Request) bool {
	isAdmin, present := r.Context().Value(isAdminKey).(bool)
	return present && isAdmin
}
<|endoftext|>


<commit_before>package profile

import (
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/cache"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/service/profile/model"
	"github.com/hackform/governor/service/user/gate"
	"github.com/labstack/echo"
	"github.com/sirupsen/logrus"
	"net/http"
)

type (
	reqProfileGetID struct {
		Userid string `json:"userid"`
	}

	reqProfileModel struct {
		Userid string `json:"userid"`
		Email  string `json:"contact_email"`
		Bio    string `json:"bio"`
		Image  string `json:"image"`
	}

	resProfileModel struct {
		Email string `json:"contact_email"`
		Bio   string `json:"bio"`
		Image string `json:"image"`
	}
)

func (r *reqProfileGetID) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	return nil
}

func (r *reqProfileModel) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	if err := validEmail(r.Email); err != nil {
		return err
	}
	if err := validBio(r.Email); err != nil {
		return err
	}
	if err := validImage(r.Image); err != nil {
		return err
	}
	return nil
}

const (
	moduleID = "profile"
)

type (
	// Profile is a service for storing user profile information
	Profile struct {
		db    *db.Database
		cache *cache.Cache
		gate  *gate.Gate
	}
)

// New creates a new Profile service
func New(conf governor.Config, l *logrus.Logger, db *db.Database, ch *cache.Cache) *Profile {
	ca := conf.Conf().GetStringMapString("userauth")

	l.Info("initialized profile service")

	return &Profile{
		db:    db,
		cache: ch,
		gate:  gate.New(ca["secret"], ca["issuer"]),
	}
}

// Mount is a collection of routes for accessing and modifying profile data
func (p *Profile) Mount(conf governor.Config, r *echo.Group, l *logrus.Logger) error {
	db := p.db.DB()

	r.POST("/", func(c echo.Context) error {
		rprofile := &reqProfileModel{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m := &profilemodel.Model{
			Email: rprofile.Email,
			Bio:   rprofile.Bio,
			Image: rprofile.Image,
		}

		if err := m.SetIDB64(rprofile.Userid); err != nil {
			err.SetErrorUser()
			return err
		}

		if err := m.Insert(db); err != nil {
			if err.Code() == 3 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, p.gate.Owner("id"))

	r.PUT("/:id", func(c echo.Context) error {
		rprofile := &reqProfileModel{}
		if err := c.Bind(rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Param("id")
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
		}

		m.Email = rprofile.Email
		m.Bio = rprofile.Bio
		m.Image = rprofile.Image

		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, p.gate.Owner("id"))

	r.GET("/:id", func(c echo.Context) error {
		rprofile := &reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
		}

		return c.JSON(http.StatusOK, &resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	})

	l.Info("mounted profile service")

	return nil
}

// Health is a check for service health
func (p *Profile) Health() *governor.Error {
	return nil
}
<commit_msg>bug fix profile post new profile<commit_after>package profile

import (
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/cache"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/service/profile/model"
	"github.com/hackform/governor/service/user/gate"
	"github.com/labstack/echo"
	"github.com/sirupsen/logrus"
	"net/http"
)

type (
	reqProfileGetID struct {
		Userid string `json:"userid"`
	}

	reqProfileModel struct {
		Userid string `json:"userid"`
		Email  string `json:"contact_email"`
		Bio    string `json:"bio"`
		Image  string `json:"image"`
	}

	resProfileModel struct {
		Email string `json:"contact_email"`
		Bio   string `json:"bio"`
		Image string `json:"image"`
	}
)

func (r *reqProfileGetID) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	return nil
}

func (r *reqProfileModel) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	if err := validEmail(r.Email); err != nil {
		return err
	}
	if err := validBio(r.Email); err != nil {
		return err
	}
	if err := validImage(r.Image); err != nil {
		return err
	}
	return nil
}

const (
	moduleID = "profile"
)

type (
	// Profile is a service for storing user profile information
	Profile struct {
		db    *db.Database
		cache *cache.Cache
		gate  *gate.Gate
	}
)

// New creates a new Profile service
func New(conf governor.Config, l *logrus.Logger, db *db.Database, ch *cache.Cache) *Profile {
	ca := conf.Conf().GetStringMapString("userauth")

	l.Info("initialized profile service")

	return &Profile{
		db:    db,
		cache: ch,
		gate:  gate.New(ca["secret"], ca["issuer"]),
	}
}

// Mount is a collection of routes for accessing and modifying profile data
func (p *Profile) Mount(conf governor.Config, r *echo.Group, l *logrus.Logger) error {
	db := p.db.DB()

	r.POST("/:id", func(c echo.Context) error {
		rprofile := &reqProfileModel{}
		if err := c.Bind(rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Param("id")
		if err := rprofile.valid(); err != nil {
			return err
		}

		m := &profilemodel.Model{
			Email: rprofile.Email,
			Bio:   rprofile.Bio,
			Image: rprofile.Image,
		}

		if err := m.SetIDB64(rprofile.Userid); err != nil {
			err.SetErrorUser()
			return err
		}

		if err := m.Insert(db); err != nil {
			if err.Code() == 3 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, p.gate.Owner("id"))

	r.PUT("/:id", func(c echo.Context) error {
		rprofile := &reqProfileModel{}
		if err := c.Bind(rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Param("id")
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
		}

		m.Email = rprofile.Email
		m.Bio = rprofile.Bio
		m.Image = rprofile.Image

		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, p.gate.Owner("id"))

	r.GET("/:id", func(c echo.Context) error {
		rprofile := &reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
		}

		return c.JSON(http.StatusOK, &resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	})

	l.Info("mounted profile service")

	return nil
}

// Health is a check for service health
func (p *Profile) Health() *governor.Error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves() (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour {
			s.RaiseIssue(ctx, fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val), false)
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:D3":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves()
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && r.Properties == req.Properties {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig(f string) (*pb.Config, error) {
	toload := &pb.Config{}
	bytes, _ := ioutil.ReadFile(f)
	proto.UnmarshalText(string(bytes), toload)
	return toload, nil
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<commit_msg>Address change in bedroom Mac Address. This closes #589<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"time"

	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type getter interface {
	getSlaves() (*pbd.ServiceList, error)
	getJobs(context.Context, *pbd.RegistryEntry) ([]*pbs.JobAssignment, error)
	getConfig(context.Context, *pbd.RegistryEntry) ([]*pbs.Requirement, error)
}

type checker interface {
	assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config)
	discover() *pbd.ServiceList
	master(entry *pbd.RegistryEntry, master bool) (bool, error)
	setprev([]string)
	getprev() []string
}

func getFleetStatus(ctx context.Context, c checker) (map[string]*pbs.JobList, map[string]*pbs.Config) {
	resJ := make(map[string]*pbs.JobList)
	resC := make(map[string]*pbs.Config)

	curr := make([]string, 0)
	for _, service := range c.discover().Services {
		if service.Name == "gobuildslave" {
			curr = append(curr, service.Identifier)
			joblist, config := c.assess(ctx, service.Identifier)
			resJ[service.Identifier] = joblist
			resC[service.Identifier] = config
		}
	}

	c.setprev(curr)

	return resJ, resC
}

// Find the first available server
func chooseServer(ctx context.Context, job *pbs.JobSpec, c checker) string {
	services := c.discover().Services
	for _, i := range rand.Perm(len(services)) {
		service := services[i]
		if service.Name == "gobuildslave" && (job.GetServer() == "" || job.GetServer() == service.GetIdentifier()) {
			jobs, sc := c.assess(ctx, service.Identifier)

			//Don't accept a server which is already running this job
			jobfine := true
			for _, j := range jobs.Details {
				if j.Spec.Name == job.Name {
					jobfine = false
				}

			}
			if jobfine {
				if sc.Disk > job.Disk && (!job.External || sc.External) && (!job.GetCds() || sc.GetSupportsCds()) {
					return service.Identifier
				}
			}
		}
	}
	return ""
}

func (s *Server) addAccessPoint(ctx context.Context, ap string) {
	s.accessPointsMutex.Lock()
	defer s.accessPointsMutex.Unlock()

	for key, val := range s.accessPoints {
		if time.Now().Sub(val) > time.Hour {
			s.RaiseIssue(ctx, fmt.Sprintf("Access point Missing"), fmt.Sprintf("%v has been missing since %v", key, val), false)
		}
	}

	switch ap {
	case "70:3A:CB:17:CF:BB":
		s.accessPoints["LR2"] = time.Now()
	case "70:3A:CB:17:CC:CF":
		s.accessPoints["Bedroom"] = time.Now()
	case "70:3A:CB:17:CE:E3":
		s.accessPoints["LR"] = time.Now()
	case "70:3A:CB:17:CF:BF":
		s.accessPoints["LR2"] = time.Now()
	}
}

// Find the first available server
func (s *Server) selectServer(ctx context.Context, job *pbs.Job, g getter) string {
	services, _ := g.getSlaves()
	for _, i := range rand.Perm(len(services.Services)) {
		jobs, _ := g.getJobs(ctx, services.Services[i])
		//Don't accept a server which is already running this job
		jobfine := true
		for _, j := range jobs {
			if j.Job.Name == job.Name {
				jobfine = false
			}

		}
		if jobfine {
			requirements, err := g.getConfig(ctx, services.Services[i])
			if err == nil {
				allmatch := true
				for _, req := range job.Requirements {
					localmatch := false
					for _, r := range requirements {
						if r.Category == pbs.RequirementCategory_ACCESS_POINT {
							s.addAccessPoint(ctx, r.Properties)
						}
						if r.Category == req.Category && r.Properties == req.Properties {
							localmatch = true

						}
					}

					if !localmatch {
						allmatch = false
					}
				}
				if allmatch {
					return services.Services[i].Identifier
				}
			}
		}
	}
	return ""
}

func configDiff(cm, cs *pb.Config) *pb.Config {
	retConfig := &pb.Config{}

	for _, entry := range cm.Intents {
		nIntent := &pb.Intent{}
		nIntent.Spec = entry.Spec
		nIntent.Count = entry.Count
		retConfig.Intents = append(retConfig.Intents, nIntent)
	}

	for _, entry := range cs.Intents {
		for _, pair := range retConfig.Intents {
			if entry.Spec.Name == pair.Spec.Name {
				pair.Count -= entry.Count
			}
		}
	}

	return retConfig
}

func loadConfig(f string) (*pb.Config, error) {
	toload := &pb.Config{}
	bytes, _ := ioutil.ReadFile(f)
	proto.UnmarshalText(string(bytes), toload)
	return toload, nil
}

func runJobs(c *pb.Config) []*pbs.JobSpec {
	var jobs []*pbs.JobSpec
	for _, j := range c.Intents {
		for i := 0; i < int(j.Count); i++ {
			jobs = append(jobs, j.Spec)
		}
	}

	return jobs
}
<|endoftext|>


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

package option

import (
	"time"

	"github.com/spf13/viper"
)

const (
	// EndpointGCIntervalDefault is the default time for the CEP GC
	EndpointGCIntervalDefault = 5 * time.Minute
)

const (
	// CNPNodeStatusGCInterval is the GC interval for nodes which have been
	// removed from the cluster in CiliumNetworkPolicy and
	// CiliumClusterwideNetworkPolicy Status.
	CNPNodeStatusGCInterval = "cnp-node-status-gc-interval"

	// CNPStatusUpdateInterval is the interval between status updates
	// being sent to the K8s apiserver for a given CNP.
	CNPStatusUpdateInterval = "cnp-status-update-interval"

	// EnableMetrics enables prometheus metrics.
	EnableMetrics = "enable-metrics"

	// EndpointGCInterval is the interval between attempts of the CEP GC
	// controller.
	// Note that only one node per cluster should run this, and most iterations
	// will simply return.
	EndpointGCInterval = "cilium-endpoint-gc-interval"

	// IdentityGCInterval is the interval in which allocator identities are
	// attempted to be expired from the kvstore
	IdentityGCInterval = "identity-gc-interval"

	// IdentityGCRateInterval is the interval used for rate limiting the GC of
	// identities.
	IdentityGCRateInterval = "identity-gc-rate-interval"

	// IdentityGCRateLimit is the maximum identities used for rate limiting the
	// GC of identities.
	IdentityGCRateLimit = "identity-gc-rate-limit"

	// IdentityHeartbeatTimeout is the timeout used to GC identities from k8s
	IdentityHeartbeatTimeout = "identity-heartbeat-timeout"

	// NodesGCInterval is the duration for which the nodes are GC in the KVStore.
	NodesGCInterval = "nodes-gc-interval"

	// OperatorAPIServeAddr IP:Port on which to serve api requests in
	// operator (pass ":Port" to bind on all interfaces, "" is off)
	OperatorAPIServeAddr = "operator-api-serve-addr"

	// OperatorPrometheusServeAddr IP:Port on which to serve prometheus
	// metrics (pass ":Port" to bind on all interfaces, "" is off).
	OperatorPrometheusServeAddr = "operator-prometheus-serve-addr"

	// SyncK8sServices synchronizes k8s services into the kvstore
	SyncK8sServices = "synchronize-k8s-services"

	// SyncK8sNodes synchronizes k8s nodes into the kvstore
	SyncK8sNodes = "synchronize-k8s-nodes"

	// UnmanagedPodWatcherInterval is the interval to check for unmanaged kube-dns pods (0 to disable)
	UnmanagedPodWatcherInterval = "unmanaged-pod-watcher-interval"

	// IPAM options

	// IPAMAPIBurst is the burst value allowed when accessing external IPAM APIs
	IPAMAPIBurst = "limit-ipam-api-burst"

	// IPAMAPIQPSLimit is the queries per second limit when accessing external IPAM APIs
	IPAMAPIQPSLimit = "limit-ipam-api-qps"

	// IPAMSubnetsIDs are optional subnets IDs used to filter subnets and interfaces listing
	IPAMSubnetsIDs = "subnet-ids-filter"

	// IPAMSubnetsTags are optional tags used to filter subnets, and interfaces within those subnets
	IPAMSubnetsTags = "subnet-tags-filter"

	// IPAMOperatorV4CIDR is the cluster IPv4 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV4CIDR = "cluster-pool-ipv4-cidr"

	// IPAMOperatorV6CIDR is the cluster IPv6 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV6CIDR = "cluster-pool-ipv6-cidr"

	// NodeCIDRMaskSizeIPv4 is the IPv4 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv4 = "cluster-pool-ipv4-mask-size"

	// NodeCIDRMaskSizeIPv6 is the IPv6 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv6 = "cluster-pool-ipv6-mask-size"

	// AWS options

	// AWSInstanceLimitMapping allows overwirting AWS instance limits defined in
	// pkg/aws/eni/limits.go
	// e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"}
	AWSInstanceLimitMapping = "aws-instance-limit-mapping"

	// AWSReleaseExcessIPs allows releasing excess free IP addresses from ENI.
	// Enabling this option reduces waste of IP addresses but may increase
	// the number of API calls to AWS EC2 service.
	AWSReleaseExcessIPs = "aws-release-excess-ips"

	// ENITags are the tags that will be added to every ENI created by the
	// AWS ENI IPAM.
	ENITags = "eni-tags"

	// ParallelAllocWorkers specifies the number of parallel workers to be used for IPAM allocation
	ParallelAllocWorkers = "parallel-alloc-workers"

	// UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2
	// API to fill out the instnacetype to adapter limit mapping.
	UpdateEC2AdapterLimitViaAPI = "update-ec2-apdater-limit-via-api"

	// Azure options

	// AzureSubscriptionID is the subscription ID to use when accessing the Azure API
	AzureSubscriptionID = "azure-subscription-id"

	// AzureResourceGroup is the resource group of the nodes used for the cluster
	AzureResourceGroup = "azure-resource-group"

	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
	CRDWaitTimeout = "crd-wait-timeout"

	// LeaderElectionLeaseDuration is the duration that non-leader candidates will wait to
	// force acquire leadership
	LeaderElectionLeaseDuration = "leader-election-lease-duration"

	// LeaderElectionRenewDeadline is the duration that the current acting master in HA deployment
	// will retry refreshing leadership before giving up the lock.
	LeaderElectionRenewDeadline = "leader-election-renew-deadline"

	// LeaderElectionRetryPeriod is the duration the LeaderElector clients should wait between
	// tries of the actions in operator HA deployment.
	LeaderElectionRetryPeriod = "leader-election-retry-period"
)

// OperatorConfig is the configuration used by the operator.
type OperatorConfig struct {
	// CNPNodeStatusGCInterval is the GC interval for nodes which have been
	// removed from the cluster in CiliumNetworkPolicy and
	// CiliumClusterwideNetworkPolicy Status.
	CNPNodeStatusGCInterval time.Duration

	// CNPStatusUpdateInterval is the interval between status updates
	// being sent to the K8s apiserver for a given CNP.
	CNPStatusUpdateInterval time.Duration

	// EnableMetrics enables prometheus metrics.
	EnableMetrics bool

	// EndpointGCInterval is the interval between attempts of the CEP GC
	// controller.
	// Note that only one node per cluster should run this, and most iterations
	// will simply return.
	EndpointGCInterval time.Duration

	// IdentityGCInterval is the interval in which allocator identities are
	// attempted to be expired from the kvstore
	IdentityGCInterval time.Duration

	// IdentityGCRateInterval is the interval used for rate limiting the GC of
	// identities.
	IdentityGCRateInterval time.Duration

	// IdentityGCRateLimit is the maximum identities used for rate limiting the
	// GC of identities.
	IdentityGCRateLimit int64

	// IdentityHeartbeatTimeout is the timeout used to GC identities from k8s
	IdentityHeartbeatTimeout time.Duration

	// NodesGCInterval is the duration for which the nodes are GC in the KVStore.
	NodesGCInterval time.Duration

	OperatorAPIServeAddr        string
	OperatorPrometheusServeAddr string

	// SyncK8sServices synchronizes k8s services into the kvstore
	SyncK8sServices bool

	// SyncK8sNodes synchronizes k8s nodes into the kvstore
	SyncK8sNodes bool

	// UnmanagedPodWatcherInterval is the interval to check for unmanaged kube-dns pods (0 to disable)
	UnmanagedPodWatcherInterval int

	// IPAM options

	// IPAMAPIBurst is the burst value allowed when accessing external IPAM APIs
	IPAMAPIBurst int

	// IPAMAPIQPSLimit is the queries per second limit when accessing external IPAM APIs
	IPAMAPIQPSLimit float64

	// IPAMSubnetsIDs are optional subnets IDs used to filter subnets and interfaces listing
	IPAMSubnetsIDs []string

	// IPAMSubnetsTags are optional tags used to filter subnets, and interfaces within those subnets
	IPAMSubnetsTags map[string]string

	// IPAM Operator options

	// IPAMOperatorV4CIDR is the cluster IPv4 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV4CIDR []string

	// IPAMOperatorV6CIDR is the cluster IPv6 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV6CIDR []string

	// NodeCIDRMaskSizeIPv4 is the IPv4 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv4 int

	// NodeCIDRMaskSizeIPv6 is the IPv6 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv6 int

	// AWS options

	// ENITags are the tags that will be added to every ENI created by the AWS ENI IPAM
	ENITags map[string]string

	// ParallelAllocWorkers specifies the number of parallel workers to be used in ENI mode.
	ParallelAllocWorkers int64

	// AWSInstanceLimitMapping allows overwriting AWS instance limits defined in
	// pkg/aws/eni/limits.go
	// e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"}
	AWSInstanceLimitMapping map[string]string

	// AWSReleaseExcessIps allows releasing excess free IP addresses from ENI.
	// Enabling this option reduces waste of IP addresses but may increase
	// the number of API calls to AWS EC2 service.
	AWSReleaseExcessIPs bool

	// UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2 API to fill out the instnacetype to adapter limit mapping
	UpdateEC2AdapterLimitViaAPI bool

	// Azure options

	// AzureSubscriptionID is the subscription ID to use when accessing the Azure API
	AzureSubscriptionID string

	// AzureResourceGroup is the resource group of the nodes used for the cluster
	AzureResourceGroup string

	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
	CRDWaitTimeout time.Duration

	// LeaderElectionLeaseDuration is the duration that non-leader candidates will wait to
	// force acquire leadership in Cilium Operator HA deployment.
	LeaderElectionLeaseDuration time.Duration

	// LeaderElectionRenewDeadline is the duration that the current acting master in HA deployment
	// will retry refreshing leadership in before giving up the lock.
	LeaderElectionRenewDeadline time.Duration

	// LeaderElectionRetryPeriod is the duration that LeaderElector clients should wait between
	// retries of the actions in operator HA deployment.
	LeaderElectionRetryPeriod time.Duration
}

// Populate sets all options with the values from viper.
func (c *OperatorConfig) Populate() {
	c.CNPNodeStatusGCInterval = viper.GetDuration(CNPNodeStatusGCInterval)
	c.CNPStatusUpdateInterval = viper.GetDuration(CNPStatusUpdateInterval)
	c.EnableMetrics = viper.GetBool(EnableMetrics)
	c.EndpointGCInterval = viper.GetDuration(EndpointGCInterval)
	c.IdentityGCInterval = viper.GetDuration(IdentityGCInterval)
	c.IdentityGCRateInterval = viper.GetDuration(IdentityGCRateInterval)
	c.IdentityGCRateLimit = viper.GetInt64(IdentityGCRateLimit)
	c.IdentityHeartbeatTimeout = viper.GetDuration(IdentityHeartbeatTimeout)
	c.NodesGCInterval = viper.GetDuration(NodesGCInterval)
	c.OperatorAPIServeAddr = viper.GetString(OperatorAPIServeAddr)
	c.OperatorPrometheusServeAddr = viper.GetString(OperatorPrometheusServeAddr)
	c.SyncK8sServices = viper.GetBool(SyncK8sServices)
	c.SyncK8sNodes = viper.GetBool(SyncK8sNodes)
	c.UnmanagedPodWatcherInterval = viper.GetInt(UnmanagedPodWatcherInterval)
	c.NodeCIDRMaskSizeIPv4 = viper.GetInt(NodeCIDRMaskSizeIPv4)
	c.NodeCIDRMaskSizeIPv6 = viper.GetInt(NodeCIDRMaskSizeIPv6)
	c.IPAMOperatorV4CIDR = viper.GetStringSlice(IPAMOperatorV4CIDR)
	c.IPAMOperatorV6CIDR = viper.GetStringSlice(IPAMOperatorV6CIDR)
	c.NodesGCInterval = viper.GetDuration(NodesGCInterval)
	c.CRDWaitTimeout = viper.GetDuration(CRDWaitTimeout)
	c.LeaderElectionLeaseDuration = viper.GetDuration(LeaderElectionLeaseDuration)
	c.LeaderElectionRenewDeadline = viper.GetDuration(LeaderElectionRenewDeadline)
	c.LeaderElectionRetryPeriod = viper.GetDuration(LeaderElectionRetryPeriod)

	// AWS options

	c.AWSReleaseExcessIPs = viper.GetBool(AWSReleaseExcessIPs)
	c.UpdateEC2AdapterLimitViaAPI = viper.GetBool(UpdateEC2AdapterLimitViaAPI)

	// Azure options

	c.AzureSubscriptionID = viper.GetString(AzureSubscriptionID)
	c.AzureResourceGroup = viper.GetString(AzureResourceGroup)

	// Option maps and slices

	if m := viper.GetStringSlice(IPAMSubnetsIDs); len(m) != 0 {
		c.IPAMSubnetsIDs = m
	}

	if m := viper.GetStringMapString(IPAMSubnetsTags); len(m) != 0 {
		c.IPAMSubnetsTags = m
	}

	if m := viper.GetStringMapString(AWSInstanceLimitMapping); len(m) != 0 {
		c.AWSInstanceLimitMapping = m
	}

	if m := viper.GetStringMapString(ENITags); len(m) != 0 {
		c.ENITags = m
	}
}

// Config represents the operator configuration.
var Config = &OperatorConfig{
	IPAMSubnetsIDs:          make([]string, 0),
	IPAMSubnetsTags:         make(map[string]string),
	AWSInstanceLimitMapping: make(map[string]string),
	ENITags:                 make(map[string]string),
}
<commit_msg>operator/option: fix typo in UpdateEC2AdapterLimitViaAPI godoc comment<commit_after>// Copyright 2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package option

import (
	"time"

	"github.com/spf13/viper"
)

const (
	// EndpointGCIntervalDefault is the default time for the CEP GC
	EndpointGCIntervalDefault = 5 * time.Minute
)

const (
	// CNPNodeStatusGCInterval is the GC interval for nodes which have been
	// removed from the cluster in CiliumNetworkPolicy and
	// CiliumClusterwideNetworkPolicy Status.
	CNPNodeStatusGCInterval = "cnp-node-status-gc-interval"

	// CNPStatusUpdateInterval is the interval between status updates
	// being sent to the K8s apiserver for a given CNP.
	CNPStatusUpdateInterval = "cnp-status-update-interval"

	// EnableMetrics enables prometheus metrics.
	EnableMetrics = "enable-metrics"

	// EndpointGCInterval is the interval between attempts of the CEP GC
	// controller.
	// Note that only one node per cluster should run this, and most iterations
	// will simply return.
	EndpointGCInterval = "cilium-endpoint-gc-interval"

	// IdentityGCInterval is the interval in which allocator identities are
	// attempted to be expired from the kvstore
	IdentityGCInterval = "identity-gc-interval"

	// IdentityGCRateInterval is the interval used for rate limiting the GC of
	// identities.
	IdentityGCRateInterval = "identity-gc-rate-interval"

	// IdentityGCRateLimit is the maximum identities used for rate limiting the
	// GC of identities.
	IdentityGCRateLimit = "identity-gc-rate-limit"

	// IdentityHeartbeatTimeout is the timeout used to GC identities from k8s
	IdentityHeartbeatTimeout = "identity-heartbeat-timeout"

	// NodesGCInterval is the duration for which the nodes are GC in the KVStore.
	NodesGCInterval = "nodes-gc-interval"

	// OperatorAPIServeAddr IP:Port on which to serve api requests in
	// operator (pass ":Port" to bind on all interfaces, "" is off)
	OperatorAPIServeAddr = "operator-api-serve-addr"

	// OperatorPrometheusServeAddr IP:Port on which to serve prometheus
	// metrics (pass ":Port" to bind on all interfaces, "" is off).
	OperatorPrometheusServeAddr = "operator-prometheus-serve-addr"

	// SyncK8sServices synchronizes k8s services into the kvstore
	SyncK8sServices = "synchronize-k8s-services"

	// SyncK8sNodes synchronizes k8s nodes into the kvstore
	SyncK8sNodes = "synchronize-k8s-nodes"

	// UnmanagedPodWatcherInterval is the interval to check for unmanaged kube-dns pods (0 to disable)
	UnmanagedPodWatcherInterval = "unmanaged-pod-watcher-interval"

	// IPAM options

	// IPAMAPIBurst is the burst value allowed when accessing external IPAM APIs
	IPAMAPIBurst = "limit-ipam-api-burst"

	// IPAMAPIQPSLimit is the queries per second limit when accessing external IPAM APIs
	IPAMAPIQPSLimit = "limit-ipam-api-qps"

	// IPAMSubnetsIDs are optional subnets IDs used to filter subnets and interfaces listing
	IPAMSubnetsIDs = "subnet-ids-filter"

	// IPAMSubnetsTags are optional tags used to filter subnets, and interfaces within those subnets
	IPAMSubnetsTags = "subnet-tags-filter"

	// IPAMOperatorV4CIDR is the cluster IPv4 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV4CIDR = "cluster-pool-ipv4-cidr"

	// IPAMOperatorV6CIDR is the cluster IPv6 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV6CIDR = "cluster-pool-ipv6-cidr"

	// NodeCIDRMaskSizeIPv4 is the IPv4 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv4 = "cluster-pool-ipv4-mask-size"

	// NodeCIDRMaskSizeIPv6 is the IPv6 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv6 = "cluster-pool-ipv6-mask-size"

	// AWS options

	// AWSInstanceLimitMapping allows overwirting AWS instance limits defined in
	// pkg/aws/eni/limits.go
	// e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"}
	AWSInstanceLimitMapping = "aws-instance-limit-mapping"

	// AWSReleaseExcessIPs allows releasing excess free IP addresses from ENI.
	// Enabling this option reduces waste of IP addresses but may increase
	// the number of API calls to AWS EC2 service.
	AWSReleaseExcessIPs = "aws-release-excess-ips"

	// ENITags are the tags that will be added to every ENI created by the
	// AWS ENI IPAM.
	ENITags = "eni-tags"

	// ParallelAllocWorkers specifies the number of parallel workers to be used for IPAM allocation
	ParallelAllocWorkers = "parallel-alloc-workers"

	// UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2
	// API to fill out the instancetype to adapter limit mapping.
	UpdateEC2AdapterLimitViaAPI = "update-ec2-apdater-limit-via-api"

	// Azure options

	// AzureSubscriptionID is the subscription ID to use when accessing the Azure API
	AzureSubscriptionID = "azure-subscription-id"

	// AzureResourceGroup is the resource group of the nodes used for the cluster
	AzureResourceGroup = "azure-resource-group"

	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
	CRDWaitTimeout = "crd-wait-timeout"

	// LeaderElectionLeaseDuration is the duration that non-leader candidates will wait to
	// force acquire leadership
	LeaderElectionLeaseDuration = "leader-election-lease-duration"

	// LeaderElectionRenewDeadline is the duration that the current acting master in HA deployment
	// will retry refreshing leadership before giving up the lock.
	LeaderElectionRenewDeadline = "leader-election-renew-deadline"

	// LeaderElectionRetryPeriod is the duration the LeaderElector clients should wait between
	// tries of the actions in operator HA deployment.
	LeaderElectionRetryPeriod = "leader-election-retry-period"
)

// OperatorConfig is the configuration used by the operator.
type OperatorConfig struct {
	// CNPNodeStatusGCInterval is the GC interval for nodes which have been
	// removed from the cluster in CiliumNetworkPolicy and
	// CiliumClusterwideNetworkPolicy Status.
	CNPNodeStatusGCInterval time.Duration

	// CNPStatusUpdateInterval is the interval between status updates
	// being sent to the K8s apiserver for a given CNP.
	CNPStatusUpdateInterval time.Duration

	// EnableMetrics enables prometheus metrics.
	EnableMetrics bool

	// EndpointGCInterval is the interval between attempts of the CEP GC
	// controller.
	// Note that only one node per cluster should run this, and most iterations
	// will simply return.
	EndpointGCInterval time.Duration

	// IdentityGCInterval is the interval in which allocator identities are
	// attempted to be expired from the kvstore
	IdentityGCInterval time.Duration

	// IdentityGCRateInterval is the interval used for rate limiting the GC of
	// identities.
	IdentityGCRateInterval time.Duration

	// IdentityGCRateLimit is the maximum identities used for rate limiting the
	// GC of identities.
	IdentityGCRateLimit int64

	// IdentityHeartbeatTimeout is the timeout used to GC identities from k8s
	IdentityHeartbeatTimeout time.Duration

	// NodesGCInterval is the duration for which the nodes are GC in the KVStore.
	NodesGCInterval time.Duration

	OperatorAPIServeAddr        string
	OperatorPrometheusServeAddr string

	// SyncK8sServices synchronizes k8s services into the kvstore
	SyncK8sServices bool

	// SyncK8sNodes synchronizes k8s nodes into the kvstore
	SyncK8sNodes bool

	// UnmanagedPodWatcherInterval is the interval to check for unmanaged kube-dns pods (0 to disable)
	UnmanagedPodWatcherInterval int

	// IPAM options

	// IPAMAPIBurst is the burst value allowed when accessing external IPAM APIs
	IPAMAPIBurst int

	// IPAMAPIQPSLimit is the queries per second limit when accessing external IPAM APIs
	IPAMAPIQPSLimit float64

	// IPAMSubnetsIDs are optional subnets IDs used to filter subnets and interfaces listing
	IPAMSubnetsIDs []string

	// IPAMSubnetsTags are optional tags used to filter subnets, and interfaces within those subnets
	IPAMSubnetsTags map[string]string

	// IPAM Operator options

	// IPAMOperatorV4CIDR is the cluster IPv4 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV4CIDR []string

	// IPAMOperatorV6CIDR is the cluster IPv6 podCIDR that should be used to
	// allocate pods in the node.
	IPAMOperatorV6CIDR []string

	// NodeCIDRMaskSizeIPv4 is the IPv4 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv4 int

	// NodeCIDRMaskSizeIPv6 is the IPv6 podCIDR mask size that will be used
	// per node.
	NodeCIDRMaskSizeIPv6 int

	// AWS options

	// ENITags are the tags that will be added to every ENI created by the AWS ENI IPAM
	ENITags map[string]string

	// ParallelAllocWorkers specifies the number of parallel workers to be used in ENI mode.
	ParallelAllocWorkers int64

	// AWSInstanceLimitMapping allows overwriting AWS instance limits defined in
	// pkg/aws/eni/limits.go
	// e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"}
	AWSInstanceLimitMapping map[string]string

	// AWSReleaseExcessIps allows releasing excess free IP addresses from ENI.
	// Enabling this option reduces waste of IP addresses but may increase
	// the number of API calls to AWS EC2 service.
	AWSReleaseExcessIPs bool

	// UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2 API to fill out the
	// instancetype to adapter limit mapping.
	UpdateEC2AdapterLimitViaAPI bool

	// Azure options

	// AzureSubscriptionID is the subscription ID to use when accessing the Azure API
	AzureSubscriptionID string

	// AzureResourceGroup is the resource group of the nodes used for the cluster
	AzureResourceGroup string

	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
	CRDWaitTimeout time.Duration

	// LeaderElectionLeaseDuration is the duration that non-leader candidates will wait to
	// force acquire leadership in Cilium Operator HA deployment.
	LeaderElectionLeaseDuration time.Duration

	// LeaderElectionRenewDeadline is the duration that the current acting master in HA deployment
	// will retry refreshing leadership in before giving up the lock.
	LeaderElectionRenewDeadline time.Duration

	// LeaderElectionRetryPeriod is the duration that LeaderElector clients should wait between
	// retries of the actions in operator HA deployment.
	LeaderElectionRetryPeriod time.Duration
}

// Populate sets all options with the values from viper.
func (c *OperatorConfig) Populate() {
	c.CNPNodeStatusGCInterval = viper.GetDuration(CNPNodeStatusGCInterval)
	c.CNPStatusUpdateInterval = viper.GetDuration(CNPStatusUpdateInterval)
	c.EnableMetrics = viper.GetBool(EnableMetrics)
	c.EndpointGCInterval = viper.GetDuration(EndpointGCInterval)
	c.IdentityGCInterval = viper.GetDuration(IdentityGCInterval)
	c.IdentityGCRateInterval = viper.GetDuration(IdentityGCRateInterval)
	c.IdentityGCRateLimit = viper.GetInt64(IdentityGCRateLimit)
	c.IdentityHeartbeatTimeout = viper.GetDuration(IdentityHeartbeatTimeout)
	c.NodesGCInterval = viper.GetDuration(NodesGCInterval)
	c.OperatorAPIServeAddr = viper.GetString(OperatorAPIServeAddr)
	c.OperatorPrometheusServeAddr = viper.GetString(OperatorPrometheusServeAddr)
	c.SyncK8sServices = viper.GetBool(SyncK8sServices)
	c.SyncK8sNodes = viper.GetBool(SyncK8sNodes)
	c.UnmanagedPodWatcherInterval = viper.GetInt(UnmanagedPodWatcherInterval)
	c.NodeCIDRMaskSizeIPv4 = viper.GetInt(NodeCIDRMaskSizeIPv4)
	c.NodeCIDRMaskSizeIPv6 = viper.GetInt(NodeCIDRMaskSizeIPv6)
	c.IPAMOperatorV4CIDR = viper.GetStringSlice(IPAMOperatorV4CIDR)
	c.IPAMOperatorV6CIDR = viper.GetStringSlice(IPAMOperatorV6CIDR)
	c.NodesGCInterval = viper.GetDuration(NodesGCInterval)
	c.CRDWaitTimeout = viper.GetDuration(CRDWaitTimeout)
	c.LeaderElectionLeaseDuration = viper.GetDuration(LeaderElectionLeaseDuration)
	c.LeaderElectionRenewDeadline = viper.GetDuration(LeaderElectionRenewDeadline)
	c.LeaderElectionRetryPeriod = viper.GetDuration(LeaderElectionRetryPeriod)

	// AWS options

	c.AWSReleaseExcessIPs = viper.GetBool(AWSReleaseExcessIPs)
	c.UpdateEC2AdapterLimitViaAPI = viper.GetBool(UpdateEC2AdapterLimitViaAPI)

	// Azure options

	c.AzureSubscriptionID = viper.GetString(AzureSubscriptionID)
	c.AzureResourceGroup = viper.GetString(AzureResourceGroup)

	// Option maps and slices

	if m := viper.GetStringSlice(IPAMSubnetsIDs); len(m) != 0 {
		c.IPAMSubnetsIDs = m
	}

	if m := viper.GetStringMapString(IPAMSubnetsTags); len(m) != 0 {
		c.IPAMSubnetsTags = m
	}

	if m := viper.GetStringMapString(AWSInstanceLimitMapping); len(m) != 0 {
		c.AWSInstanceLimitMapping = m
	}

	if m := viper.GetStringMapString(ENITags); len(m) != 0 {
		c.ENITags = m
	}
}

// Config represents the operator configuration.
var Config = &OperatorConfig{
	IPAMSubnetsIDs:          make([]string, 0),
	IPAMSubnetsTags:         make(map[string]string),
	AWSInstanceLimitMapping: make(map[string]string),
	ENITags:                 make(map[string]string),
}
<|endoftext|>


<commit_before>package userService

import (
	"github.com/ewhal/nyaa/db"
	"github.com/ewhal/nyaa/model"
	"errors"
	"net/http"

	"github.com/ewhal/nyaa/util/log"
)

// FindUserByUserName creates a user.
func FindUserByUserName(userName string) (model.User, int, error) {
	var user model.User
	var err error
	if db.ORM.Where("name=?", appID, userName).First(&user).RecordNotFound() {
		return user, http.StatusUnauthorized, err
	}
	return user, http.StatusOK, nil
}

// FindOrCreateUser creates a user.
func FindOrCreateUser(username string) (model.User, int, error) {
	var user model.User
	var err error
	if db.ORM.Where("username=?", username).First(&user).RecordNotFound() {
		var user model.User
		user.Username = username
		log.Debugf("user %+v\n", user)
		if db.ORM.Create(&user).Error != nil {
			return user, http.StatusBadRequest, errors.New("User is not created.")
		}
		log.Debugf("retrived User %v\n", user)
		return user, http.StatusOK, nil
	}
	return user, http.StatusBadRequest, nil
}
<commit_msg>Fix #2<commit_after>package userService

import (
	"github.com/ewhal/nyaa/db"
	"github.com/ewhal/nyaa/model"
	"errors"
	"net/http"

	"github.com/ewhal/nyaa/util/log"
)

// FindUserByUserName creates a user.
func FindUserByUserName(userName string) (model.User, int, error) {
	var user model.User
	var err error
	if db.ORM.Where("name=?", userName).First(&user).RecordNotFound() {
		return user, http.StatusUnauthorized, err
	}
	return user, http.StatusOK, nil
}

// FindOrCreateUser creates a user.
func FindOrCreateUser(username string) (model.User, int, error) {
	var user model.User
	var err error
	if db.ORM.Where("username=?", username).First(&user).RecordNotFound() {
		var user model.User
		user.Username = username
		log.Debugf("user %+v\n", user)
		if db.ORM.Create(&user).Error != nil {
			return user, http.StatusBadRequest, errors.New("User is not created.")
		}
		log.Debugf("retrived User %v\n", user)
		return user, http.StatusOK, nil
	}
	return user, http.StatusBadRequest, nil
}
<|endoftext|>


<commit_before>package pearl

import (
	cryptorand "crypto/rand"
	"fmt"
	"io"
	"math/big"
	"math/rand"
	"time"

	"github.com/mmcloughlin/pearl/torkeys"
)

type ConnectionCertificates interface {
}

func GenerateConnectionCertificates(idKey torkeys.PrivateKey) (ConnectionCertificates, error) {
	// Reference: https://github.com/torproject/torspec/blob/master/tor-spec.txt#L225-L245
	//
	//	   In "in-protocol" (a.k.a. "the v3 handshake"), the initiator sends no
	//	   certificates, and the
	//	   responder sends a single connection certificate.  The choice of
	//	   ciphersuites must be as in a "renegotiation" handshake.  There are
	//	   additionally a set of constraints on the connection certificate,
	//	   which the initiator can use to learn that the in-protocol handshake
	//	   is in use.  Specifically, at least one of these properties must be
	//	   true of the certificate:
	//	      * The certificate is self-signed
	//	      * Some component other than "commonName" is set in the subject or
	//	        issuer DN of the certificate.
	//	      * The commonName of the subject or issuer of the certificate ends
	//	        with a suffix other than ".net".
	//	      * The certificate's public key modulus is longer than 1024 bits.
	//	   The initiator then sends a VERSIONS cell to the responder, which then
	//	   replies with a VERSIONS cell; they have then negotiated a Tor
	//	   protocol version.  Assuming that the version they negotiate is 3 or higher
	//	   (the only ones specified for use with this handshake right now), the
	//	   responder sends a CERTS cell, an AUTH_CHALLENGE cell, and a NETINFO
	//	   cell to the initiator, which may send either CERTS, AUTHENTICATE,
	//	   NETINFO if it wants to authenticate, or just NETINFO if it does not.
	//
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L1061-L1066
	//
	//	  nickname = crypto_random_hostname(8, 20, "www.", ".net");
	//	#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
	//	  nn2 = crypto_random_hostname(8, 20, "www.", ".net");
	//	#else
	//	  nn2 = crypto_random_hostname(8, 20, "www.", ".com");
	//	#endif
	//

	linkCN := randomHostname(8, 20, "www.", ".net")
	idCN := randomHostname(8, 20, "www.", ".com")

	// Certificate lifetime is either set by the SSLKeyLifetime option or
	// generated to a reasonable looking value.
	//
	// BUG(mmcloughlin): SSLKeyLifetime option ignored when generating
	// certificates.
	lifetime := generateCertificateLifetime()

	fmt.Println(linkCN, idCN, lifetime)

	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L1080-L1082
	//
	//	    /* Create a link certificate signed by identity key. */
	//	    cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
	//	                                      key_lifetime);
	//

	//linkCert, err := openssl.NewCertificate(&openssl.CertificateInfo{
	//	CommonName: linkCN,
	//	Serial:     rand.Int63(),
	//	Issued:     issued,
	//	Expires:    expires,
	//}, tmpPk)
	//if err != nil {
	//	return nil, err
	//}

	return nil, nil
}

// randomHostname generates a hostname starting with prefix, ending with
// suffix, and of length between min and max (inclusive).
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/crypto.c#L3172-L3181
//
//	/** Generate and return a new random hostname starting with prefix,
//	 * ending with suffix, and containing no fewer than
//	 * min_rand_len and no more than max_rand_len random base32
//	 * characters. Does not check for failure.
//	 *
//	 * Clip max_rand_len to MAX_DNS_LABEL_SIZE.
//	 **/
//	char *
//	crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix,
//	                       const char *suffix)
//
func randomHostname(min, max int, prefix, suffix string) string {
	// Reference: https://github.com/torproject/tor/blob/master/src/common/util_format.h#L23-L25
	//
	//	/** Characters that can appear (case-insensitively) in a base32 encoding. */
	//	#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
	//	void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
	//
	alphabet := "abcdefghijklmnopqrstuvwxyz234567"
	n := min + rand.Intn(max-min+1)
	b := make([]byte, n)
	for i := 0; i < n; i++ {
		b[i] = alphabet[rand.Intn(len(alphabet))]
	}
	return prefix + string(b) + suffix
}

// generateCertificateLifetime generates a reasonable looking certificate
// lifetime.
//
// Reference: https://github.com/torproject/tor/blob/master/src/or/router.c#L702-L717
//
//	  if (!lifetime) { /* we should guess a good ssl cert lifetime */
//
//	    /* choose between 5 and 365 days, and round to the day */
//	    unsigned int five_days = 5*24*3600;
//	    unsigned int one_year = 365*24*3600;
//	    lifetime = crypto_rand_int_range(five_days, one_year);
//	    lifetime -= lifetime % (24*3600);
//
//	    if (crypto_rand_int(2)) {
//	      /* Half the time we expire at midnight, and half the time we expire
//	       * one second before midnight. (Some CAs wobble their expiry times a
//	       * bit in practice, perhaps to reduce collision attacks; see ticket
//	       * 8443 for details about observed certs in the wild.) */
//	      lifetime--;
//	    }
//	  }
//
func generateCertificateLifetime() time.Duration {
	days := 5 + rand.Intn(360)
	wobble := rand.Intn(2)
	return time.Duration(days*24)*time.Hour - time.Duration(wobble)*time.Second
}

// generateCertificateSerial generates a serial number for a certificate. This
// copies the convention of openssl and returns a 64-bit integer. Returns
// big.Int so it can be used with
// https://godoc.org/github.com/mmcloughlin/openssl#CertificateInfo.
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L468-L470
//
//	  /* OpenSSL generates self-signed certificates with random 64-bit serial
//	   * numbers, so let's do that too. */
//	#define SERIAL_NUMBER_SIZE 8
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L502-L508
//
//	  { /* our serial number is 8 random bytes. */
//	    crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
//	    if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
//	      goto error;
//	    if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
//	      goto error;
//	  }
//
func generateCertificateSerial() (*big.Int, error) {
	return generateCertificateSerialFromRandom(cryptorand.Reader)
}

func generateCertificateSerialFromRandom(r io.Reader) (*big.Int, error) {
	serialBytes := make([]byte, 8)
	_, err := io.ReadFull(r, serialBytes)
	if err != nil {
		return nil, err
	}
	serial := big.NewInt(0)
	return serial.SetBytes(serialBytes), nil
}
<commit_msg>cert gen<commit_after>package pearl

import (
	cryptorand "crypto/rand"
	"io"
	"math/big"
	"math/rand"
	"time"

	"github.com/mmcloughlin/openssl"
	"github.com/mmcloughlin/pearl/torkeys"
)

type TLSContext struct {
	IDCert   *openssl.Certificate
	LinkKey  openssl.PrivateKey
	LinkCert *openssl.Certificate
	AuthKey  openssl.PrivateKey
	AuthCert *openssl.Certificate
}

func NewTLSContext(idKey openssl.PrivateKey) (*TLSContext, error) {
	ctx := &TLSContext{}

	// Reference: https://github.com/torproject/torspec/blob/master/tor-spec.txt#L225-L245
	//
	//	   In "in-protocol" (a.k.a. "the v3 handshake"), the initiator sends no
	//	   certificates, and the
	//	   responder sends a single connection certificate.  The choice of
	//	   ciphersuites must be as in a "renegotiation" handshake.  There are
	//	   additionally a set of constraints on the connection certificate,
	//	   which the initiator can use to learn that the in-protocol handshake
	//	   is in use.  Specifically, at least one of these properties must be
	//	   true of the certificate:
	//	      * The certificate is self-signed
	//	      * Some component other than "commonName" is set in the subject or
	//	        issuer DN of the certificate.
	//	      * The commonName of the subject or issuer of the certificate ends
	//	        with a suffix other than ".net".
	//	      * The certificate's public key modulus is longer than 1024 bits.
	//	   The initiator then sends a VERSIONS cell to the responder, which then
	//	   replies with a VERSIONS cell; they have then negotiated a Tor
	//	   protocol version.  Assuming that the version they negotiate is 3 or higher
	//	   (the only ones specified for use with this handshake right now), the
	//	   responder sends a CERTS cell, an AUTH_CHALLENGE cell, and a NETINFO
	//	   cell to the initiator, which may send either CERTS, AUTHENTICATE,
	//	   NETINFO if it wants to authenticate, or just NETINFO if it does not.
	//
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L1061-L1066
	//
	//	  nickname = crypto_random_hostname(8, 20, "www.", ".net");
	//	#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
	//	  nn2 = crypto_random_hostname(8, 20, "www.", ".net");
	//	#else
	//	  nn2 = crypto_random_hostname(8, 20, "www.", ".com");
	//	#endif
	//

	linkCN := randomHostname(8, 20, "www.", ".net")
	idCN := randomHostname(8, 20, "www.", ".com")

	// Generate identity certificate.
	//
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L1083-L1085
	//
	//	    /* Create self-signed certificate for identity key. */
	//	    idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
	//	                                        IDENTITY_CERT_LIFETIME);
	//
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L67-L68
	//
	//	/** How long do identity certificates live? (sec) */
	//	#define IDENTITY_CERT_LIFETIME  (365*24*60*60)
	//

	idLifetime := time.Duration(365*24) * time.Hour

	var err error
	ctx.IDCert, err = generateCertificate(idCN, idKey, idLifetime)
	if err != nil {
		return nil, err
	}

	err = setIssuerAndSignCertificate(ctx.IDCert, ctx.IDCert, idKey)
	if err != nil {
		return nil, err
	}

	// Certificate lifetime is either set by the SSLKeyLifetime option or
	// generated to a reasonable looking value.
	//
	// BUG(mmcloughlin): SSLKeyLifetime option ignored when generating
	// certificates.
	lifetime := generateCertificateLifetime()

	// Generate link certificate.
	//
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L1080-L1082
	//
	//	    /* Create a link certificate signed by identity key. */
	//	    cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
	//	                                      key_lifetime);
	//

	ctx.LinkKey, err = torkeys.GenerateRSA()
	if err != nil {
		return nil, err
	}

	ctx.LinkCert, err = generateCertificate(linkCN, ctx.LinkKey, lifetime)
	if err != nil {
		return nil, err
	}

	err = setIssuerAndSignCertificate(ctx.LinkCert, ctx.IDCert, idKey)
	if err != nil {
		return nil, err
	}

	// Generate auth certificate.

	ctx.AuthKey, err = torkeys.GenerateRSA()
	if err != nil {
		return nil, err
	}

	ctx.AuthCert, err = generateCertificate(linkCN, ctx.AuthKey, lifetime)
	if err != nil {
		return nil, err
	}

	err = setIssuerAndSignCertificate(ctx.AuthCert, ctx.IDCert, idKey)
	if err != nil {
		return nil, err
	}

	return ctx, nil
}

func generateCertificate(cn string, key openssl.PrivateKey, lifetime time.Duration) (*openssl.Certificate, error) {
	serial, err := generateCertificateSerial()
	if err != nil {
		return nil, err
	}

	now := time.Now()
	issued := generateCertificateIssued(now, lifetime)
	issuedDuration := issued.Sub(now)

	return openssl.NewCertificate(&openssl.CertificateInfo{
		CommonName: cn,
		Serial:     serial,
		Issued:     issuedDuration,
		Expires:    issuedDuration + lifetime,
	}, key)
}

func setIssuerAndSignCertificate(cert, issuer *openssl.Certificate, key openssl.PrivateKey) error {
	err := cert.SetIssuer(issuer)
	if err != nil {
		return err
	}

	err = cert.Sign(key, openssl.EVP_SHA256)
	if err != nil {
		return err
	}

	return nil
}

// randomHostname generates a hostname starting with prefix, ending with
// suffix, and of length between min and max (inclusive).
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/crypto.c#L3172-L3181
//
//	/** Generate and return a new random hostname starting with prefix,
//	 * ending with suffix, and containing no fewer than
//	 * min_rand_len and no more than max_rand_len random base32
//	 * characters. Does not check for failure.
//	 *
//	 * Clip max_rand_len to MAX_DNS_LABEL_SIZE.
//	 **/
//	char *
//	crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix,
//	                       const char *suffix)
//
func randomHostname(min, max int, prefix, suffix string) string {
	// Reference: https://github.com/torproject/tor/blob/master/src/common/util_format.h#L23-L25
	//
	//	/** Characters that can appear (case-insensitively) in a base32 encoding. */
	//	#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
	//	void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
	//
	alphabet := "abcdefghijklmnopqrstuvwxyz234567"
	n := min + rand.Intn(max-min+1)
	b := make([]byte, n)
	for i := 0; i < n; i++ {
		b[i] = alphabet[rand.Intn(len(alphabet))]
	}
	return prefix + string(b) + suffix
}

// generateCertificateLifetime generates a reasonable looking certificate
// lifetime.
//
// Reference: https://github.com/torproject/tor/blob/master/src/or/router.c#L702-L717
//
//	  if (!lifetime) { /* we should guess a good ssl cert lifetime */
//
//	    /* choose between 5 and 365 days, and round to the day */
//	    unsigned int five_days = 5*24*3600;
//	    unsigned int one_year = 365*24*3600;
//	    lifetime = crypto_rand_int_range(five_days, one_year);
//	    lifetime -= lifetime % (24*3600);
//
//	    if (crypto_rand_int(2)) {
//	      /* Half the time we expire at midnight, and half the time we expire
//	       * one second before midnight. (Some CAs wobble their expiry times a
//	       * bit in practice, perhaps to reduce collision attacks; see ticket
//	       * 8443 for details about observed certs in the wild.) */
//	      lifetime--;
//	    }
//	  }
//
func generateCertificateLifetime() time.Duration {
	days := 5 + rand.Intn(360)
	wobble := rand.Intn(2)
	return time.Duration(days*24)*time.Hour - time.Duration(wobble)*time.Second
}

// generateCertificateIssued computes when we pretend a certificate was
// issued, given the total lifetime of the certificate.
func generateCertificateIssued(now time.Time, lifetime time.Duration) time.Time {
	// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L481-L487
	//
	//	  /* Make sure we're part-way through the certificate lifetime, rather
	//	   * than having it start right now. Don't choose quite uniformly, since
	//	   * then we might pick a time where we're about to expire. Lastly, be
	//	   * sure to start on a day boundary. */
	//	  time_t now = time(NULL);
	//	  start_time = crypto_rand_time_range(now - cert_lifetime, now) + 2*24*3600;
	//	  start_time -= start_time % (24*3600);
	//

	// BUG(mmcloughlin): certificate issued time not correctly computed
	return now.Add(-lifetime / 2)
}

// generateCertificateSerial generates a serial number for a certificate. This
// copies the convention of openssl and returns a 64-bit integer. Returns
// big.Int so it can be used with
// https://godoc.org/github.com/mmcloughlin/openssl#CertificateInfo.
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L468-L470
//
//	  /* OpenSSL generates self-signed certificates with random 64-bit serial
//	   * numbers, so let's do that too. */
//	#define SERIAL_NUMBER_SIZE 8
//
// Reference: https://github.com/torproject/tor/blob/master/src/common/tortls.c#L502-L508
//
//	  { /* our serial number is 8 random bytes. */
//	    crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
//	    if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
//	      goto error;
//	    if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
//	      goto error;
//	  }
//
func generateCertificateSerial() (*big.Int, error) {
	return generateCertificateSerialFromRandom(cryptorand.Reader)
}

func generateCertificateSerialFromRandom(r io.Reader) (*big.Int, error) {
	serialBytes := make([]byte, 8)
	_, err := io.ReadFull(r, serialBytes)
	if err != nil {
		return nil, err
	}
	serial := big.NewInt(0)
	return serial.SetBytes(serialBytes), nil
}
<|endoftext|>


<commit_before>/*
 * Spreed Speak Freely.
 * Copyright (C) 2013-2014 struktur AG
 *
 * This file is part of Spreed Speak Freely.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gorilla/securecookie"
	"log"
	"sync"
	"time"
)

const (
	turnTTL               = 3600 // XXX(longsleep): Add to config file.
	maxBroadcastPerSecond = 1000
	maxUsersLength        = 5000
)

type MessageRequest struct {
	From    string
	To      string
	Message []byte
	Id      string
}

type HubStat struct {
	Rooms            int                  `json:"rooms"`
	Connections      int                  `json:"connections"`
	Users            int                  `json:"users"`
	Count            uint64               `json:"count"`
	IdsInRoom        map[string][]string  `json:"idsinroom,omitempty"`
	UsersById        map[string]*DataUser `json:"usersbyid,omitempty"`
	ConnectionsByIdx map[string]string    `json:"connectionsbyidx,omitempty"`
}

type Hub struct {
	server          *Server
	connectionTable map[string]*Connection
	userTable       map[string]*User
	roomTable       map[string]*RoomWorker
	version         string
	config          *Config
	sessionSecret   []byte
	turnSecret      []byte
	tickets         *securecookie.SecureCookie
	count           uint64
	mutex           sync.RWMutex
}

func NewHub(version string, config *Config, sessionSecret string, turnSecret string) *Hub {

	h := &Hub{
		connectionTable: make(map[string]*Connection),
		userTable:       make(map[string]*User),
		roomTable:       make(map[string]*RoomWorker),
		version:         version,
		config:          config,
		sessionSecret:   []byte(sessionSecret),
		turnSecret:      []byte(turnSecret),
	}

	h.tickets = securecookie.New(h.sessionSecret, nil)
	return h

}

func (h *Hub) Stat(details bool) *HubStat {
	h.mutex.RLock()
	defer h.mutex.RUnlock()
	stat := &HubStat{
		Rooms:       len(h.roomTable),
		Connections: len(h.connectionTable),
		Users:       len(h.userTable),
		Count:       h.count,
	}
	if details {
		rooms := make(map[string][]string)
		for roomid, room := range h.roomTable {
			users := make([]string, 0, len(room.connections))
			for id, _ := range room.connections {
				users = append(users, id)
			}
			rooms[roomid] = users
		}
		stat.IdsInRoom = rooms
		users := make(map[string]*DataUser)
		for userid, user := range h.userTable {
			users[userid] = user.Data()
		}
		stat.UsersById = users
		connections := make(map[string]string)
		for id, connection := range h.connectionTable {
			connections[fmt.Sprintf("%d", connection.Idx)] = id
		}
		stat.ConnectionsByIdx = connections
	}
	return stat
}

func (h *Hub) CreateTurnData(id string) *DataTurn {

	// Create turn data credentials for shared secret auth with TURN
	// server. See http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
	// and https://code.google.com/p/rfc5766-turn-server/ REST API auth
	// and set shared secret in TURN server with static-auth-secret.
	if len(h.turnSecret) == 0 {
		return &DataTurn{}
	}
	foo := hmac.New(sha1.New, h.turnSecret)
	user := fmt.Sprintf("%s:%d", id, int32(time.Now().Unix()))
	foo.Write([]byte(user))
	password := base64.StdEncoding.EncodeToString(foo.Sum(nil))
	return &DataTurn{user, password, turnTTL, h.config.TurnURIs}

}

func (h *Hub) EncodeTicket(key, value string) (string, error) {

	if value == "" {
		// Create new id.
		value = fmt.Sprintf("%s", securecookie.GenerateRandomKey(16))
	}
	return h.tickets.Encode(key, value)

}

func (h *Hub) DecodeTicket(key, value string) (string, error) {

	result := ""
	err := h.tickets.Decode(key, value, &result)
	return result, err

}

func (h *Hub) GetRoom(id string) *RoomWorker {

	h.mutex.RLock()
	room, ok := h.roomTable[id]
	if !ok {
		h.mutex.RUnlock()
		h.mutex.Lock()
		room = NewRoomWorker(h, id)
		h.roomTable[id] = room
		h.mutex.Unlock()
		go func() {
			// Start room, this blocks until room expired.
			room.Start()
			// Cleanup room when we are done.
			h.mutex.Lock()
			defer h.mutex.Unlock()
			delete(h.roomTable, id)
			log.Printf("Cleaned up room '%s'\n", id)
		}()
	} else {
		h.mutex.RUnlock()
	}

	return room

}

func (h *Hub) GetGlobalConnections() []*Connection {

	if h.config.globalRoomid == "" {
		return make([]*Connection, 0)
	}
	h.mutex.RLock()
	if room, ok := h.roomTable[h.config.globalRoomid]; ok {
		h.mutex.RUnlock()
		return room.GetConnections()
	} else {
		h.mutex.RUnlock()
	}
	return make([]*Connection, 0)

}

func (h *Hub) RunForAllRooms(f func(room *RoomWorker)) {

	h.mutex.RLock()
	for _, room := range h.roomTable {
		f(room)
	}
	h.mutex.RUnlock()

}

func (h *Hub) isGlobalRoomid(id string) bool {

	return id != "" && (id == h.config.globalRoomid)

}

func (h *Hub) isDefaultRoomid(id string) bool {

	return id == ""
}

func (h *Hub) registerHandler(c *Connection) {

	h.mutex.Lock()

	// Create new user instance.
	h.count++
	c.Idx = h.count
	u := &User{Id: c.Id}
	h.userTable[c.Id] = u
	c.User = u
	c.IsRegistered = true

	// Register connection or replace existing one.
	if ec, ok := h.connectionTable[c.Id]; ok {
		delete(h.connectionTable, ec.Id)
		ec.IsRegistered = false
		ec.close()
		h.connectionTable[c.Id] = c
		h.mutex.Unlock()
		//log.Printf("Register (%d) from %s: %s (existing)\n", c.Idx, c.RemoteAddr, c.Id)
	} else {
		h.connectionTable[c.Id] = c
		//fmt.Println("registered", c.Id)
		h.mutex.Unlock()
		//log.Printf("Register (%d) from %s: %s\n", c.Idx, c.RemoteAddr, c.Id)
		h.server.OnRegister(c)
	}

}

func (h *Hub) unregisterHandler(c *Connection) {

	h.mutex.Lock()
	if !c.IsRegistered {
		h.mutex.Unlock()
		return
	}
	c.close()
	delete(h.connectionTable, c.Id)
	delete(h.userTable, c.Id)
	h.mutex.Unlock()
	//log.Printf("Unregister (%d) from %s: %s\n", c.Idx, c.RemoteAddr, c.Id)
	h.server.OnUnregister(c)

}

func (h *Hub) unicastHandler(m *MessageRequest) {

	h.mutex.RLock()
	out, ok := h.connectionTable[m.To]
	h.mutex.RUnlock()
	if !ok {
		log.Println("Unicast To not found", m.To)
		return
	}
	out.send(m.Message)

}

func (h *Hub) aliveHandler(c *Connection, alive *DataAlive) {

	aliveJson, err := json.Marshal(&DataOutgoing{From: c.Id, Data: alive})
	if err != nil {
		log.Println("Alive error while encoding JSON", err)
		return
	}
	c.send(aliveJson)

}

func (h *Hub) userupdateHandler(u *UserUpdate) uint64 {

	//fmt.Println("Userupdate", u)
	h.mutex.RLock()
	user, ok := h.userTable[u.Id]
	h.mutex.RUnlock()
	var rev uint64
	if ok {
		rev = user.Update(u)
	} else {
		log.Printf("Update data for unknown user %s\n", u.Id)
	}
	return rev

}
<commit_msg>Prevent duplicate room creation.<commit_after>/*
 * Spreed Speak Freely.
 * Copyright (C) 2013-2014 struktur AG
 *
 * This file is part of Spreed Speak Freely.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gorilla/securecookie"
	"log"
	"sync"
	"time"
)

const (
	turnTTL               = 3600 // XXX(longsleep): Add to config file.
	maxBroadcastPerSecond = 1000
	maxUsersLength        = 5000
)

type MessageRequest struct {
	From    string
	To      string
	Message []byte
	Id      string
}

type HubStat struct {
	Rooms            int                  `json:"rooms"`
	Connections      int                  `json:"connections"`
	Users            int                  `json:"users"`
	Count            uint64               `json:"count"`
	IdsInRoom        map[string][]string  `json:"idsinroom,omitempty"`
	UsersById        map[string]*DataUser `json:"usersbyid,omitempty"`
	ConnectionsByIdx map[string]string    `json:"connectionsbyidx,omitempty"`
}

type Hub struct {
	server          *Server
	connectionTable map[string]*Connection
	userTable       map[string]*User
	roomTable       map[string]*RoomWorker
	version         string
	config          *Config
	sessionSecret   []byte
	turnSecret      []byte
	tickets         *securecookie.SecureCookie
	count           uint64
	mutex           sync.RWMutex
}

func NewHub(version string, config *Config, sessionSecret string, turnSecret string) *Hub {

	h := &Hub{
		connectionTable: make(map[string]*Connection),
		userTable:       make(map[string]*User),
		roomTable:       make(map[string]*RoomWorker),
		version:         version,
		config:          config,
		sessionSecret:   []byte(sessionSecret),
		turnSecret:      []byte(turnSecret),
	}

	h.tickets = securecookie.New(h.sessionSecret, nil)
	return h

}

func (h *Hub) Stat(details bool) *HubStat {
	h.mutex.RLock()
	defer h.mutex.RUnlock()
	stat := &HubStat{
		Rooms:       len(h.roomTable),
		Connections: len(h.connectionTable),
		Users:       len(h.userTable),
		Count:       h.count,
	}
	if details {
		rooms := make(map[string][]string)
		for roomid, room := range h.roomTable {
			users := make([]string, 0, len(room.connections))
			for id, _ := range room.connections {
				users = append(users, id)
			}
			rooms[roomid] = users
		}
		stat.IdsInRoom = rooms
		users := make(map[string]*DataUser)
		for userid, user := range h.userTable {
			users[userid] = user.Data()
		}
		stat.UsersById = users
		connections := make(map[string]string)
		for id, connection := range h.connectionTable {
			connections[fmt.Sprintf("%d", connection.Idx)] = id
		}
		stat.ConnectionsByIdx = connections
	}
	return stat
}

func (h *Hub) CreateTurnData(id string) *DataTurn {

	// Create turn data credentials for shared secret auth with TURN
	// server. See http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
	// and https://code.google.com/p/rfc5766-turn-server/ REST API auth
	// and set shared secret in TURN server with static-auth-secret.
	if len(h.turnSecret) == 0 {
		return &DataTurn{}
	}
	foo := hmac.New(sha1.New, h.turnSecret)
	user := fmt.Sprintf("%s:%d", id, int32(time.Now().Unix()))
	foo.Write([]byte(user))
	password := base64.StdEncoding.EncodeToString(foo.Sum(nil))
	return &DataTurn{user, password, turnTTL, h.config.TurnURIs}

}

func (h *Hub) EncodeTicket(key, value string) (string, error) {

	if value == "" {
		// Create new id.
		value = fmt.Sprintf("%s", securecookie.GenerateRandomKey(16))
	}
	return h.tickets.Encode(key, value)

}

func (h *Hub) DecodeTicket(key, value string) (string, error) {

	result := ""
	err := h.tickets.Decode(key, value, &result)
	return result, err

}

func (h *Hub) GetRoom(id string) *RoomWorker {

	h.mutex.RLock()
	room, ok := h.roomTable[id]
	if !ok {
		h.mutex.RUnlock()
		h.mutex.Lock()
		// need to re-check, another thread might have created the room
		// while we waited for the lock
		room, ok = h.roomTable[id]
		if !ok {
			room = NewRoomWorker(h, id)
			h.roomTable[id] = room
			h.mutex.Unlock()
			go func() {
				// Start room, this blocks until room expired.
				room.Start()
				// Cleanup room when we are done.
				h.mutex.Lock()
				defer h.mutex.Unlock()
				delete(h.roomTable, id)
				log.Printf("Cleaned up room '%s'\n", id)
			}()
		} else {
			h.mutex.Unlock()
		}
	} else {
		h.mutex.RUnlock()
	}

	return room

}

func (h *Hub) GetGlobalConnections() []*Connection {

	if h.config.globalRoomid == "" {
		return make([]*Connection, 0)
	}
	h.mutex.RLock()
	if room, ok := h.roomTable[h.config.globalRoomid]; ok {
		h.mutex.RUnlock()
		return room.GetConnections()
	} else {
		h.mutex.RUnlock()
	}
	return make([]*Connection, 0)

}

func (h *Hub) RunForAllRooms(f func(room *RoomWorker)) {

	h.mutex.RLock()
	for _, room := range h.roomTable {
		f(room)
	}
	h.mutex.RUnlock()

}

func (h *Hub) isGlobalRoomid(id string) bool {

	return id != "" && (id == h.config.globalRoomid)

}

func (h *Hub) isDefaultRoomid(id string) bool {

	return id == ""
}

func (h *Hub) registerHandler(c *Connection) {

	h.mutex.Lock()

	// Create new user instance.
	h.count++
	c.Idx = h.count
	u := &User{Id: c.Id}
	h.userTable[c.Id] = u
	c.User = u
	c.IsRegistered = true

	// Register connection or replace existing one.
	if ec, ok := h.connectionTable[c.Id]; ok {
		delete(h.connectionTable, ec.Id)
		ec.IsRegistered = false
		ec.close()
		h.connectionTable[c.Id] = c
		h.mutex.Unlock()
		//log.Printf("Register (%d) from %s: %s (existing)\n", c.Idx, c.RemoteAddr, c.Id)
	} else {
		h.connectionTable[c.Id] = c
		//fmt.Println("registered", c.Id)
		h.mutex.Unlock()
		//log.Printf("Register (%d) from %s: %s\n", c.Idx, c.RemoteAddr, c.Id)
		h.server.OnRegister(c)
	}

}

func (h *Hub) unregisterHandler(c *Connection) {

	h.mutex.Lock()
	if !c.IsRegistered {
		h.mutex.Unlock()
		return
	}
	c.close()
	delete(h.connectionTable, c.Id)
	delete(h.userTable, c.Id)
	h.mutex.Unlock()
	//log.Printf("Unregister (%d) from %s: %s\n", c.Idx, c.RemoteAddr, c.Id)
	h.server.OnUnregister(c)

}

func (h *Hub) unicastHandler(m *MessageRequest) {

	h.mutex.RLock()
	out, ok := h.connectionTable[m.To]
	h.mutex.RUnlock()
	if !ok {
		log.Println("Unicast To not found", m.To)
		return
	}
	out.send(m.Message)

}

func (h *Hub) aliveHandler(c *Connection, alive *DataAlive) {

	aliveJson, err := json.Marshal(&DataOutgoing{From: c.Id, Data: alive})
	if err != nil {
		log.Println("Alive error while encoding JSON", err)
		return
	}
	c.send(aliveJson)

}

func (h *Hub) userupdateHandler(u *UserUpdate) uint64 {

	//fmt.Println("Userupdate", u)
	h.mutex.RLock()
	user, ok := h.userTable[u.Id]
	h.mutex.RUnlock()
	var rev uint64
	if ok {
		rev = user.Update(u)
	} else {
		log.Printf("Update data for unknown user %s\n", u.Id)
	}
	return rev

}
<|endoftext|>