<commit_before>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/rs/xid"
)

const (
	// Read buffer size for websocket upgrader
	readBufferSize = 1024
	// Write buffer size for websocker upgrader
	writeBufferSize = 1024
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

var (
	// Websocket Upgrader
	upgrader = websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
	}
	// Guid to generate globally unique id
	guid = xid.New()
)

// Default creates a new instance of Sphere
func Default(brokers ...IBroker) *Sphere {
	// declare agent
	var broker IBroker
	// set declared agent if parameter exists
	for _, i := range brokers {
		broker = i
		break
	}
	if broker == nil {
		broker = DefaultSimpleBroker()
	}
	// creates sphere instance
	sphere := &Sphere{
		broker:      broker,
		connections: newConnectionMap(),
		channels:    newChannelMap(),
		models:      newChannelModelMap(),
		events:      newEventModelMap(),
	}
	return sphere
}

// Sphere represents an entire Websocket instance
type Sphere struct {
	// a broker agent
	broker IBroker
	// list of active connections
	connections connectionmap
	// list of channels
	channels channelmap
	// list of models
	models channelmodelmap
	// list of events
	events eventmodelmap
}

// Handler handles and creates websocket connection
func (sphere *Sphere) Handler(w http.ResponseWriter, r *http.Request) {
	if conn, err := NewConnection(w, r); err == nil {
		sphere.connections.Set(conn.id, conn)
		// run connection queue
		go conn.queue()
		// action after connection disconnected
		defer func() {
			// unsubscribe all channels
			for item := range conn.channels.Iter() {
				channel := item.Val
				sphere.unsubscribe(channel.namespace, channel.room, conn)
			}
			// close all send and receive buffers
			conn.close()
			// remove connection from sphere after disconnect
			sphere.connections.Remove(conn.id)
		}()
		for {
			_, msg, err := conn.ReadMessage()
			if err != nil {
				return
			}
			if msg != nil {
				go sphere.process(conn, msg)
			}
		}
	}
}

// Models load channel or event models
func (sphere *Sphere) Models(models ...interface{}) {
	for _, item := range models {
		switch model := item.(type) {
		case IChannels:
			if !sphere.models.Has(model.Namespace()) {
				sphere.models.Set(model.Namespace(), model)
			}
		case IEvents:
			if !sphere.events.Has(model.Namespace()) {
				sphere.events.Set(model.Namespace(), model)
			}
		}
	}
}

// process parses and processes received message
func (sphere *Sphere) process(conn *Connection, msg []byte) {
	// convert received bytes to Packet object
	p, err := ParsePacket(msg)
	if err != nil {
		LogError(err)
		return
	}
	switch p.Type {
	case PacketTypeChannel:
		if p.Namespace != "" && p.Room != "" {
			// publish message to broker if it is a channel event / message
			p.Machine = sphere.broker.ID()
			sphere.publish(p, conn)
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeSubscribe:
		if p.Namespace != "" && p.Room != "" {
			// subscribe connection to channel
			err := sphere.subscribe(p.Namespace, p.Room, conn)
			r := p.Response()
			r.SetError(err)
			// return success or failure message to user
			conn.send <- r
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeUnsubscribe:
		if p.Namespace != "" && p.Room != "" {
			// unsubscribe connection from channel
			if sphere.models.Has(p.Namespace) {
				sphere.unsubscribe(p.Namespace, p.Room, conn)
			} else {
				// if namespace model does not existed, return error
				p.Error = ErrNotSupported
				conn.send <- p
			}
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeMessage:
		if p.Namespace != "" {
			// receive event message
			sphere.receive(p, conn)
		} else {
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypePing:
		// ping-pong
		r := p.Response()
		conn.send <- r
	}
}

// channel returns Channel object, channel will be automatually created when autoCreateOpts is true
func (sphere *Sphere) channel(namespace string, room string, autoCreateOpts ...bool) *Channel {
	c := make(chan *Channel)
	autoCreateOpt := false
	for _, opt := range autoCreateOpts {
		autoCreateOpt = opt
		break
	}
	name := sphere.broker.ChannelName(namespace, room)
	go func() {
		if tmp, ok := sphere.channels.Get(name); ok {
			c <- tmp
		} else {
			if autoCreateOpt {
				channel := NewChannel(namespace, room)
				sphere.channels.Set(name, channel)
				c <- channel
			} else {
				c <- nil
			}
		}
	}()
	return <-c
}

// subscribe trigger Broker OnSubscribe action and put connection into channel connections list
func (sphere *Sphere) subscribe(namespace string, room string, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	if accept, err := model.Subscribe(room, conn); !accept && err == nil {
		return ErrUnauthorized
	} else if !accept && err != nil {
		return err
	}
	channel := sphere.channel(namespace, room, true)
	if channel == nil {
		return ErrNotFound
	}
	if err := channel.subscribe(conn); err != nil {
		return err
	}
	if !sphere.broker.IsSubscribed(channel.namespace, channel.room) {
		c := make(chan IError)
		go sphere.broker.OnSubscribe(channel, c)
		return <-c
	}
	return nil
}

// unsubscribe trigger Broker OnUnsubscribe action and remove connection from channel connections list
func (sphere *Sphere) unsubscribe(namespace string, room string, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	if err := model.Disconnect(room, conn); err != nil {
		return err
	}
	channel := sphere.channel(namespace, room, false)
	if channel == nil {
		return ErrNotFound
	}
	err := channel.unsubscribe(conn)
	if err == nil && channel.connections.Count() == 0 {
		if sphere.broker.IsSubscribed(channel.namespace, channel.room) {
			c := make(chan IError)
			go sphere.broker.OnUnsubscribe(channel, c)
			return <-c
		}
	}
	return err
}

// publish trigger Broker OnPublish action, send message to user from broker
func (sphere *Sphere) publish(p *Packet, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(p.Namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(p.Namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	channel := sphere.channel(p.Namespace, p.Room)
	if channel == nil {
		return ErrBadStatus
	}
	if !channel.isSubscribed(conn) {
		return ErrNotSubscribed
	}
	msg := p.Message
	if msg == nil || msg.Event == "" {
		return ErrBadScheme
	}
	res, err := model.Receive(msg.Event, msg.Data)
	if err != nil {
		return err
	}
	d := p.Response()
	if res != "" {
		d.Message.Data = res
	}
	if sphere.broker.IsSubscribed(channel.namespace, channel.room) {
		return sphere.broker.OnPublish(channel, d)
	}
	return ErrServerErrors
}

// receive message and event handler
func (sphere *Sphere) receive(p *Packet, conn *Connection) IError {
	var model IEvents
	if !sphere.events.Has(p.Namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.events.Get(p.Namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	msg := p.Message
	if msg == nil || msg.Event == "" {
		return ErrBadScheme
	}
	res, err := model.Receive(msg.Event, msg.Data)
	if err != nil {
		return err
	}
	d := p.Response()
	if res != "" {
		d.Message.Data = res
	}
	conn.send <- d
	return nil
}
<commit_msg>return error from Handler<commit_after>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/rs/xid"
)

const (
	// Read buffer size for websocket upgrader
	readBufferSize = 1024
	// Write buffer size for websocker upgrader
	writeBufferSize = 1024
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second
	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10
	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

var (
	// Websocket Upgrader
	upgrader = websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
	}
	// Guid to generate globally unique id
	guid = xid.New()
)

// Default creates a new instance of Sphere
func Default(brokers ...IBroker) *Sphere {
	// declare agent
	var broker IBroker
	// set declared agent if parameter exists
	for _, i := range brokers {
		broker = i
		break
	}
	if broker == nil {
		broker = DefaultSimpleBroker()
	}
	// creates sphere instance
	sphere := &Sphere{
		broker:      broker,
		connections: newConnectionMap(),
		channels:    newChannelMap(),
		models:      newChannelModelMap(),
		events:      newEventModelMap(),
	}
	return sphere
}

// Sphere represents an entire Websocket instance
type Sphere struct {
	// a broker agent
	broker IBroker
	// list of active connections
	connections connectionmap
	// list of channels
	channels channelmap
	// list of models
	models channelmodelmap
	// list of events
	events eventmodelmap
}

// Handler handles and creates websocket connection
func (sphere *Sphere) Handler(w http.ResponseWriter, r *http.Request) IError {
	if conn, err := NewConnection(w, r); err == nil {
		sphere.connections.Set(conn.id, conn)
		// run connection queue
		go conn.queue()
		// action after connection disconnected
		defer func() {
			// unsubscribe all channels
			for item := range conn.channels.Iter() {
				channel := item.Val
				sphere.unsubscribe(channel.namespace, channel.room, conn)
			}
			// close all send and receive buffers
			conn.close()
			// remove connection from sphere after disconnect
			sphere.connections.Remove(conn.id)
		}()
		for {
			_, msg, err := conn.ReadMessage()
			if err != nil {
				return err
			}
			if msg != nil {
				go sphere.process(conn, msg)
			}
		}
	} else {
		return err
	}
	return nil
}

// Models load channel or event models
func (sphere *Sphere) Models(models ...interface{}) {
	for _, item := range models {
		switch model := item.(type) {
		case IChannels:
			if !sphere.models.Has(model.Namespace()) {
				sphere.models.Set(model.Namespace(), model)
			}
		case IEvents:
			if !sphere.events.Has(model.Namespace()) {
				sphere.events.Set(model.Namespace(), model)
			}
		}
	}
}

// process parses and processes received message
func (sphere *Sphere) process(conn *Connection, msg []byte) {
	// convert received bytes to Packet object
	p, err := ParsePacket(msg)
	if err != nil {
		LogError(err)
		return
	}
	switch p.Type {
	case PacketTypeChannel:
		if p.Namespace != "" && p.Room != "" {
			// publish message to broker if it is a channel event / message
			p.Machine = sphere.broker.ID()
			sphere.publish(p, conn)
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeSubscribe:
		if p.Namespace != "" && p.Room != "" {
			// subscribe connection to channel
			err := sphere.subscribe(p.Namespace, p.Room, conn)
			r := p.Response()
			r.SetError(err)
			// return success or failure message to user
			conn.send <- r
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeUnsubscribe:
		if p.Namespace != "" && p.Room != "" {
			// unsubscribe connection from channel
			if sphere.models.Has(p.Namespace) {
				sphere.unsubscribe(p.Namespace, p.Room, conn)
			} else {
				// if namespace model does not existed, return error
				p.Error = ErrNotSupported
				conn.send <- p
			}
		} else {
			// if namespace or room is not provided, return error message
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypeMessage:
		if p.Namespace != "" {
			// receive event message
			sphere.receive(p, conn)
		} else {
			p.Error = ErrBadScheme
			conn.send <- p
		}
	case PacketTypePing:
		// ping-pong
		r := p.Response()
		conn.send <- r
	}
}

// channel returns Channel object, channel will be automatually created when autoCreateOpts is true
func (sphere *Sphere) channel(namespace string, room string, autoCreateOpts ...bool) *Channel {
	c := make(chan *Channel)
	autoCreateOpt := false
	for _, opt := range autoCreateOpts {
		autoCreateOpt = opt
		break
	}
	name := sphere.broker.ChannelName(namespace, room)
	go func() {
		if tmp, ok := sphere.channels.Get(name); ok {
			c <- tmp
		} else {
			if autoCreateOpt {
				channel := NewChannel(namespace, room)
				sphere.channels.Set(name, channel)
				c <- channel
			} else {
				c <- nil
			}
		}
	}()
	return <-c
}

// subscribe trigger Broker OnSubscribe action and put connection into channel connections list
func (sphere *Sphere) subscribe(namespace string, room string, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	if accept, err := model.Subscribe(room, conn); !accept && err == nil {
		return ErrUnauthorized
	} else if !accept && err != nil {
		return err
	}
	channel := sphere.channel(namespace, room, true)
	if channel == nil {
		return ErrNotFound
	}
	if err := channel.subscribe(conn); err != nil {
		return err
	}
	if !sphere.broker.IsSubscribed(channel.namespace, channel.room) {
		c := make(chan IError)
		go sphere.broker.OnSubscribe(channel, c)
		return <-c
	}
	return nil
}

// unsubscribe trigger Broker OnUnsubscribe action and remove connection from channel connections list
func (sphere *Sphere) unsubscribe(namespace string, room string, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	if err := model.Disconnect(room, conn); err != nil {
		return err
	}
	channel := sphere.channel(namespace, room, false)
	if channel == nil {
		return ErrNotFound
	}
	err := channel.unsubscribe(conn)
	if err == nil && channel.connections.Count() == 0 {
		if sphere.broker.IsSubscribed(channel.namespace, channel.room) {
			c := make(chan IError)
			go sphere.broker.OnUnsubscribe(channel, c)
			return <-c
		}
	}
	return err
}

// publish trigger Broker OnPublish action, send message to user from broker
func (sphere *Sphere) publish(p *Packet, conn *Connection) IError {
	var model IChannels
	if !sphere.models.Has(p.Namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.models.Get(p.Namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	channel := sphere.channel(p.Namespace, p.Room)
	if channel == nil {
		return ErrBadStatus
	}
	if !channel.isSubscribed(conn) {
		return ErrNotSubscribed
	}
	msg := p.Message
	if msg == nil || msg.Event == "" {
		return ErrBadScheme
	}
	res, err := model.Receive(msg.Event, msg.Data)
	if err != nil {
		return err
	}
	d := p.Response()
	if res != "" {
		d.Message.Data = res
	}
	if sphere.broker.IsSubscribed(channel.namespace, channel.room) {
		return sphere.broker.OnPublish(channel, d)
	}
	return ErrServerErrors
}

// receive message and event handler
func (sphere *Sphere) receive(p *Packet, conn *Connection) IError {
	var model IEvents
	if !sphere.events.Has(p.Namespace) {
		return ErrNotSupported
	}
	if tmp, ok := sphere.events.Get(p.Namespace); ok {
		model = tmp
	} else {
		return ErrNotSupported
	}
	msg := p.Message
	if msg == nil || msg.Event == "" {
		return ErrBadScheme
	}
	res, err := model.Receive(msg.Event, msg.Data)
	if err != nil {
		return err
	}
	d := p.Response()
	if res != "" {
		d.Message.Data = res
	}
	conn.send <- d
	return nil
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"strings"

	"google.golang.org/api/cloudbilling/v1"
	"google.golang.org/api/cloudresourcemanager/v1"
)

func GetProjectCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	// NOTE: asset.name should use the project number, but we use project_id b/c
	// the number is computed server-side.
	name, err := assetName(d, config, "//cloudresourcemanager.googleapis.com/projects/{{project_id_or_project}}")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetProjectApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: "cloudresourcemanager.googleapis.com/Project",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "Project",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetProjectApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	pid := d.Get("project_id").(string)

	project := &cloudresourcemanager.Project{
		ProjectId: pid,
		Name:      d.Get("name").(string),
	}

	if err := getParentResourceId(d, project); err != nil {
		return nil, err
	}

	if _, ok := d.GetOk("labels"); ok {
		project.Labels = expandLabels(d)
	}

	return jsonMap(project)
}

func getParentResourceId(d TerraformResourceData, p *cloudresourcemanager.Project) error {
	orgId := d.Get("org_id").(string)
	folderId := d.Get("folder_id").(string)

	if orgId != "" && folderId != "" {
		return fmt.Errorf("'org_id' and 'folder_id' cannot be both set.")
	}

	if orgId != "" {
		p.Parent = &cloudresourcemanager.ResourceId{
			Id:   orgId,
			Type: "organization",
		}
	}

	if folderId != "" {
		p.Parent = &cloudresourcemanager.ResourceId{
			Id:   strings.TrimPrefix(folderId, "folders/"),
			Type: "folder",
		}
	}

	return nil
}

func GetProjectBillingInfoCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	name, err := assetName(d, config, "//cloudbilling.googleapis.com/projects/{{project_id_or_project}}/billingInfo")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetProjectBillingInfoApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: "cloudbilling.googleapis.com/ProjectBillingInfo",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/cloudbilling/v1/rest",
				DiscoveryName:        "ProjectBillingInfo",
				Data:                 obj,
			}},
		}, nil
	} else {
		return []Asset{}, err
	}
}

func GetProjectBillingInfoApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	if _, ok := d.GetOk("billing_account"); !ok {
		// TODO: If the project already exists, we could ask the API about it's
		// billing info here.
		return nil, ErrNoConversion
	}

	ba := &cloudbilling.ProjectBillingInfo{
		BillingAccountName: fmt.Sprintf("billingAccounts/%s", d.Get("billing_account")),
		Name:               fmt.Sprintf("projects/%s/billingInfo", d.Get("project_id")),
		ProjectId:          d.Get("project_id").(string),
	}

	return jsonMap(ba)
}
<commit_msg>Made project CAI conversion prefer project number if available (#4605) (#659)<commit_after>package google

import (
	"fmt"
	"strings"

	"google.golang.org/api/cloudbilling/v1"
	"google.golang.org/api/cloudresourcemanager/v1"
)

func GetProjectCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	// use project number if it's available; otherwise, fill in project id so that we
	// keep the CAI assets apart for different uncreated projects.
	var linkTmpl string
	if _, ok := d.GetOk("number"); ok {
		linkTmpl = "//cloudresourcemanager.googleapis.com/projects/{{number}}"
	} else {
		linkTmpl = "//cloudresourcemanager.googleapis.com/projects/{{project_id_or_project}}"
	}
	name, err := assetName(d, config, linkTmpl)
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetProjectApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: "cloudresourcemanager.googleapis.com/Project",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "Project",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetProjectApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	pid := d.Get("project_id").(string)

	project := &cloudresourcemanager.Project{
		ProjectId: pid,
		Name:      d.Get("name").(string),
	}

	if err := getParentResourceId(d, project); err != nil {
		return nil, err
	}

	if _, ok := d.GetOk("labels"); ok {
		project.Labels = expandLabels(d)
	}

	return jsonMap(project)
}

func getParentResourceId(d TerraformResourceData, p *cloudresourcemanager.Project) error {
	orgId := d.Get("org_id").(string)
	folderId := d.Get("folder_id").(string)

	if orgId != "" && folderId != "" {
		return fmt.Errorf("'org_id' and 'folder_id' cannot be both set.")
	}

	if orgId != "" {
		p.Parent = &cloudresourcemanager.ResourceId{
			Id:   orgId,
			Type: "organization",
		}
	}

	if folderId != "" {
		p.Parent = &cloudresourcemanager.ResourceId{
			Id:   strings.TrimPrefix(folderId, "folders/"),
			Type: "folder",
		}
	}

	return nil
}

func GetProjectBillingInfoCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	// use project number if it's available; otherwise, fill in project id so that we
	// keep the CAI assets apart for different uncreated projects.
	var linkTmpl string
	if _, ok := d.GetOk("number"); ok {
		linkTmpl = "//cloudbilling.googleapis.com/projects/{{number}}/billingInfo"
	} else {
		linkTmpl = "//cloudbilling.googleapis.com/projects/{{project_id_or_project}}/billingInfo"
	}
	name, err := assetName(d, config, linkTmpl)
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetProjectBillingInfoApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: "cloudbilling.googleapis.com/ProjectBillingInfo",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/cloudbilling/v1/rest",
				DiscoveryName:        "ProjectBillingInfo",
				Data:                 obj,
			}},
		}, nil
	} else {
		return []Asset{}, err
	}
}

func GetProjectBillingInfoApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	if _, ok := d.GetOk("billing_account"); !ok {
		// TODO: If the project already exists, we could ask the API about it's
		// billing info here.
		return nil, ErrNoConversion
	}

	var name string
	if number, ok := d.GetOk("number"); ok {
		name = fmt.Sprintf("projects/%s/billingInfo", number)
	} else {
		name = fmt.Sprintf("projects/%s/billingInfo", d.Get("project_id"))
	}

	ba := &cloudbilling.ProjectBillingInfo{
		BillingAccountName: fmt.Sprintf("billingAccounts/%s", d.Get("billing_account")),
		Name:               name,
		ProjectId:          d.Get("project_id").(string),
	}

	return jsonMap(ba)
}
<|endoftext|>


<commit_before>package goosinfo

import (
    "fmt"
    "syscall"
)

func uname_chars_to_string(ca [65]int8) string {
    s := make([]byte, len(ca))
    var lens int
    for ; lens < len(ca); lens++ {
        if ca[lens] == 0 {
            break
        }
        s[lens] = uint8(ca[lens])
    }
    return string(s[0:lens])
}

func GetOSVersion() (string, error) {
    //FIXME: Read /etc/os-release or similar and then fall back on kernel
    //       version
    uts := syscall.Utsname{}
    err := syscall.Uname(&uts)
    if err != nil {
        return "", fmt.Errorf("Could not get Linux version")
    }
    return uname_chars_to_string(uts.Release), nil
}
<commit_msg>added support for os-release<commit_after>package goosinfo

import (
    "io/ioutil"
    "strings"
    "os"
    "fmt"
    "syscall"
)

type OSRelease struct {
    Name string
    PrettyName string
    Version string
    VersionID string
    ID string
}

func uname_chars_to_string(ca [65]int8) string {
    s := make([]byte, len(ca))
    var lens int
    for ; lens < len(ca); lens++ {
        if ca[lens] == 0 {
            break
        }
        s[lens] = uint8(ca[lens])
    }
    return string(s[0:lens])
}

func parse_os_release (contents string) (*OSRelease, error) {
    osr := OSRelease{}
    contents = strings.TrimSpace(contents)
    lines := strings.Split(contents, "\n")
    for _, line := range(lines) {
        spl := strings.Split(line, "=")
        k := strings.ToUpper(spl[0])
        v := strings.Trim(spl[1], "\"")
        if k == "NAME" {
            osr.Name = v
        } else if k == "PRETTY_NAME" {
            osr.PrettyName = v
        } else if k == "VERSION" {
            osr.Version = v
        } else if k == "VERSION_ID" {
            osr.VersionID = v
        } else if k == "ID" {
            osr.ID = v
        }
    }
    return &osr, nil
}

func read_os_release () (*OSRelease, error) {
    file, err := os.Open("/etc/os-release")
    if err != nil {
        return nil, err
    }
    c, err := ioutil.ReadAll(file)
    if err != nil {
        return nil, err
    }
    file.Close()
    return parse_os_release(string(c))
}

func get_kernel_version_uname() (string, error) {
    uts := syscall.Utsname{}
    err := syscall.Uname(&uts)
    if err != nil {
        return "", fmt.Errorf("Could not get Linux version")
    }
    y, _ := read_os_release()
    fmt.Println(y)
    return uname_chars_to_string(uts.Release), nil
}

func GetOSVersion() (string, error) {
    osr, err := read_os_release()
    if err != nil {
        kver, err := get_kernel_version_uname()
        if err != nil {
            return "", err
        }
        return kver, nil
    }
    return osr.Version, nil
}
<|endoftext|>


<commit_before>// deck: make slide decks
package main

import (
	"bufio"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/ajstarks/openvg"
)

// Deck defines the structure of a presentation deck
// The size of the canvas, and series of slides
type Deck struct {
	Canvas canvas  `xml:"canvas"`
	Slide  []slide `xml:"slide"`
}

type canvas struct {
	Width  int `xml:"width,attr"`
	Height int `xml:"height,attr"`
}

type list struct {
	Xp    float64  `xml:"xp,attr"`
	Yp    float64  `xml:"yp,attr"`
	Sp    float64  `xml:"sp,attr"`
	Type  string   `xml:"type,attr"`
	Align string   `xml:"align,attr"`
	Color string   `xml:"color,attr"`
	Li    []string `xml:"li"`
}

type text struct {
	Xp    float64 `xml:"xp,attr"`
	Yp    float64 `xml:"yp,attr"`
	Sp    float64 `xml:"sp,attr"`
	Type  string  `xml:"type,attr"`
	Align string  `xml:"align,attr"`
	Color string  `xml:"color,attr"`
	Tdata string  `xml:",chardata"`
}

type image struct {
	Xp     float64 `xml:"xp,attr"`
	Yp     float64 `xml:"yp,attr"`
	Width  int     `xml:"width,attr"`
	Height int     `xml:"height,attr"`
	Name   string  `xml:"name,attr"`
}

type slide struct {
	Bg    string  `xml:"bg,attr"`
	Fg    string  `xml:"fg,attr"`
	List  []list  `xml:"list"`
	Text  []text  `xml:"text"`
	Image []image `xml:"image"`
}

// dodeck reads and decodes slide files
func dodeck(filename string) {
	var r io.ReadCloser
	var err error
	r, err = os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	defer r.Close()

	var d Deck
	derr := xml.NewDecoder(r).Decode(&d)
	if derr != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}

	w, h := openvg.Init()
	if d.Canvas.Width == 0 {
		d.Canvas.Width = w
	}
	if d.Canvas.Height == 0 {
		d.Canvas.Height = h
	}
	interact(d)
	openvg.Finish()
	//dumpdeck(d)
}

// dumpdeck shows the decoded description
func dumpdeck(d Deck) {
	fmt.Printf("Canvas = %v\n", d.Canvas)
	for i, s := range d.Slide {
		fmt.Printf("Slide #%d = %v %v\n", i, s.Bg, s.Fg)
		for j, l := range s.List {
			fmt.Printf("\tList #%d = %#v\n", j, l)
		}
		for k, t := range s.Text {
			fmt.Printf("\tText #%d = %#v\n", k, t)
		}
		for m, im := range s.Image {
			fmt.Printf("\tImage #%d = %#v\n", m, im)
		}
	}
}

// dimen determines the coordinates and size of an object
func dimen(c canvas, xp, yp, sp float64) (x, y float64, s int) {
	x = (xp / 100) * float64(c.Width)
	y = (yp / 100) * float64(c.Height)
	s = int((sp / 100) * float64(c.Width))
	return
}

// showlide displays slides
func showslide(d Deck, n int) {
	if n < 0 || n > len(d.Slide)-1 {
		return
	}
	s := d.Slide[n]
	openvg.Start(d.Canvas.Width, d.Canvas.Height)
	if s.Bg == "" {
		s.Bg = "white"
	}
	if s.Fg == "" {
		s.Fg = "black"
	}
	openvg.BackgroundColor(s.Bg)
	openvg.FillColor(s.Fg)

	var x, y float64
	var fontsize int

	// every list in the slide
	var offset float64
	for _, l := range s.List {
		x, y, fontsize = dimen(d.Canvas, l.Xp, l.Yp, l.Sp)
		if l.Type == "bullet" {
			offset = 1.2 * float64(fontsize)
		} else {
			offset = 0
		}
		if l.Color != "" {
			openvg.FillColor(l.Color)
		} else {
			openvg.FillColor(s.Fg)
		}
		for _, li := range l.Li {
			if l.Type == "bullet" {
				boffset := float64(fontsize) / 2
				openvg.Circle(x, y+boffset, boffset)
			}
			switch l.Align {
			case "center", "middle", "mid":
				openvg.TextMid(x+offset, y, li, "sans", fontsize)
			case "right", "end":
				openvg.TextEnd(x+offset, y, li, "sans", fontsize)
			default:
				openvg.Text(x+offset, y, li, "sans", fontsize)
			}
			y -= float64(fontsize) * 2.0
		}
	}
	openvg.FillColor(s.Fg)

	// every text in the slide
	var font string
	for _, t := range s.Text {
		x, y, fontsize = dimen(d.Canvas, t.Xp, t.Yp, t.Sp)
		if t.Type == "code" {
			font = "mono"
		} else {
			font = "sans"
		}
		if t.Color != "" {
			openvg.FillColor(t.Color)
		} else {
			openvg.FillColor(s.Fg)
		}
		td := strings.Split(t.Tdata, "\n")
		for _, txt := range td {
			switch t.Align {
			case "center", "middle", "mid":
				openvg.TextMid(x, y, txt, font, fontsize)
			case "right", "end":
				openvg.TextEnd(x, y, txt, font, fontsize)
			default:
				openvg.Text(x, y, txt, font, fontsize)
			}
			y -= float64(fontsize) * 1.8
		}
	}
	openvg.FillColor(s.Fg)

	// every image in the slide
	for _, im := range s.Image {
		x = (im.Xp / 100) * float64(d.Canvas.Width)
		y = (im.Yp / 100) * float64(d.Canvas.Height)
		openvg.Image(x-float64(im.Width/2), y-float64(im.Height/2), im.Width, im.Height, im.Name)
	}
	openvg.End()
}

// interact controls the display of the deck
func interact(d Deck) {
	firstslide := 0
	lastslide := len(d.Slide) - 1
	n := firstslide
	r := bufio.NewReader(os.Stdin)

	var cmd = "0"

	for ; rune(cmd[0]) != 'q'; cmd = readcmd(r) {
		switch rune(cmd[0]) {
		case '0', '1':
			n = firstslide
			showslide(d, n)

		case '*':
			n = lastslide
			showslide(d, n)

		case '+', 'n', '\n', '\t':
			n++
			if n > lastslide {
				n = firstslide
			}
			showslide(d, n)

		case '-', 'p':
			n--
			if n < firstslide {
				n = lastslide
			}
			showslide(d, n)

		case '/':
			if len(cmd) > 2 {
				ns := searchdeck(d, n, cmd[1:len(cmd)-1])
				println("searchdeck", ns)
				if ns >= 0 {
					showslide(d, ns)
				}
			}
		}
	}
}

// searchdeck searches the deck for the specified text, returning the slide number if found
func searchdeck(d Deck, n int, s string) int {
	if n < 0 || n > len(d.Slide)-1 {
		return -1
	}
	// for every slide...
	for i := 0; i < len(d.Slide); i++ {
		// search lists
		for _, l := range d.Slide[i].List {
			for _, ll := range l.Li {
				// fmt.Printf("searching %s in list %v\n", s, ll)
				if strings.Contains(ll, s) {
					return i
				}
			}
		}
		// search text
		for _, t := range d.Slide[i].Text {
			// fmt.Printf("searching %s in text %v\n", s, t.Tdata)
			if strings.Contains(t.Tdata, s) {
				return i
			}
		}
	}
	return -1
}

// readcmd reads interaction commands
func readcmd(r *bufio.Reader) string {
	s, err := r.ReadBytes('\n')
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return "q"
	}
	return string(s)
}

// for every file, make a deck
func main() {
	if len(os.Args) > 1 {
		for _, f := range os.Args[1:] {
			dodeck(f)
		}
	}
}
<commit_msg>cleanup<commit_after>// deck: make slide decks
package main

import (
	"bufio"
	"encoding/xml"
	"fmt"
	"os"
	"strings"

	"github.com/ajstarks/openvg"
)

// Deck defines the structure of a presentation deck
// The size of the canvas, and series of slides
type Deck struct {
	Canvas canvas  `xml:"canvas"`
	Slide  []slide `xml:"slide"`
}

type canvas struct {
	Width  int `xml:"width,attr"`
	Height int `xml:"height,attr"`
}

type list struct {
	Xp    float64  `xml:"xp,attr"`
	Yp    float64  `xml:"yp,attr"`
	Sp    float64  `xml:"sp,attr"`
	Type  string   `xml:"type,attr"`
	Align string   `xml:"align,attr"`
	Color string   `xml:"color,attr"`
	Li    []string `xml:"li"`
}

type text struct {
	Xp    float64 `xml:"xp,attr"`
	Yp    float64 `xml:"yp,attr"`
	Sp    float64 `xml:"sp,attr"`
	Type  string  `xml:"type,attr"`
	Align string  `xml:"align,attr"`
	Color string  `xml:"color,attr"`
	Tdata string  `xml:",chardata"`
}

type image struct {
	Xp     float64 `xml:"xp,attr"`
	Yp     float64 `xml:"yp,attr"`
	Width  int     `xml:"width,attr"`
	Height int     `xml:"height,attr"`
	Name   string  `xml:"name,attr"`
}

type slide struct {
	Bg    string  `xml:"bg,attr"`
	Fg    string  `xml:"fg,attr"`
	List  []list  `xml:"list"`
	Text  []text  `xml:"text"`
	Image []image `xml:"image"`
}

// dodeck reads and decodes slide files
func dodeck(filename string) {
	r, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	defer r.Close()

	var d Deck
	err = xml.NewDecoder(r).Decode(&d)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}
	w, h := openvg.Init()
	if d.Canvas.Width == 0 {
		d.Canvas.Width = w
	}
	if d.Canvas.Height == 0 {
		d.Canvas.Height = h
	}
	interact(d)
	openvg.Finish()
	//dumpdeck(d)
}

// dumpdeck shows the decoded description
func dumpdeck(d Deck) {
	fmt.Printf("Canvas = %v\n", d.Canvas)
	for i, s := range d.Slide {
		fmt.Printf("Slide #%d = %v %v\n", i, s.Bg, s.Fg)
		for j, l := range s.List {
			fmt.Printf("\tList #%d = %#v\n", j, l)
		}
		for k, t := range s.Text {
			fmt.Printf("\tText #%d = %#v\n", k, t)
		}
		for m, im := range s.Image {
			fmt.Printf("\tImage #%d = %#v\n", m, im)
		}
	}
}

// dimen determines the coordinates and size of an object
func dimen(c canvas, xp, yp, sp float64) (x, y float64, s int) {
	x = (xp / 100) * float64(c.Width)
	y = (yp / 100) * float64(c.Height)
	s = int((sp / 100) * float64(c.Width))
	return
}

//showtext displays text
func showtext(x, y float64, s, align, font string, fontsize int) {
	switch align {
	case "center", "middle", "mid":
		openvg.TextMid(x, y, s, font, fontsize)
	case "right", "end":
		openvg.TextEnd(x, y, s, font, fontsize)
	default:
		openvg.Text(x, y, s, font, fontsize)
	}
}

// showlide displays slides
func showslide(d Deck, n int) {
	if n < 0 || n > len(d.Slide)-1 {
		return
	}
	s := d.Slide[n]
	openvg.Start(d.Canvas.Width, d.Canvas.Height)
	if s.Bg == "" {
		s.Bg = "white"
	}
	if s.Fg == "" {
		s.Fg = "black"
	}
	openvg.BackgroundColor(s.Bg)
	openvg.FillColor(s.Fg)

	var x, y float64
	var fontsize int

	// every list in the slide
	var offset float64
	for _, l := range s.List {
		x, y, fontsize = dimen(d.Canvas, l.Xp, l.Yp, l.Sp)
		if l.Type == "bullet" {
			offset = 1.2 * float64(fontsize)
		} else {
			offset = 0
		}
		if l.Color != "" {
			openvg.FillColor(l.Color)
		} else {
			openvg.FillColor(s.Fg)
		}
		for _, li := range l.Li {
			if l.Type == "bullet" {
				boffset := float64(fontsize) / 2
				openvg.Circle(x, y+boffset, boffset)
			}
			showtext(x+offset, y, li, l.Align, "sans", fontsize)
			y -= float64(fontsize) * 2.0
		}
	}
	openvg.FillColor(s.Fg)

	// every text in the slide
	var font string
	for _, t := range s.Text {
		x, y, fontsize = dimen(d.Canvas, t.Xp, t.Yp, t.Sp)
		if t.Type == "code" {
			font = "mono"
		} else {
			font = "sans"
		}
		if t.Color != "" {
			openvg.FillColor(t.Color)
		} else {
			openvg.FillColor(s.Fg)
		}
		td := strings.Split(t.Tdata, "\n")
		for _, txt := range td {
			showtext(x, y, txt, t.Align, font, fontsize)
			y -= float64(fontsize) * 1.8
		}
	}
	openvg.FillColor(s.Fg)

	// every image in the slide
	for _, im := range s.Image {
		x = (im.Xp / 100) * float64(d.Canvas.Width)
		y = (im.Yp / 100) * float64(d.Canvas.Height)
		openvg.Image(x-float64(im.Width/2), y-float64(im.Height/2), im.Width, im.Height, im.Name)
	}
	openvg.End()
}

// interact controls the display of the deck
func interact(d Deck) {
	firstslide := 0
	lastslide := len(d.Slide) - 1
	n := firstslide
	r := bufio.NewReader(os.Stdin)

	var cmd = "0"

	for ; rune(cmd[0]) != 'q'; cmd = readcmd(r) {
		switch rune(cmd[0]) {
		case '0', '1':
			n = firstslide
			showslide(d, n)

		case '*':
			n = lastslide
			showslide(d, n)

		case '+', 'n', '\n', '\t':
			n++
			if n > lastslide {
				n = firstslide
			}
			showslide(d, n)

		case '-', 'p':
			n--
			if n < firstslide {
				n = lastslide
			}
			showslide(d, n)

		case '/':
			if len(cmd) > 2 {
				ns := searchdeck(d, cmd[1:len(cmd)-1])
				if ns >= 0 {
					showslide(d, ns)
				}
			}
		}
	}
}

// searchdeck searches the deck for the specified text, returning the slide number if found
func searchdeck(d Deck, s string) int {
	// for every slide...
	for i := 0; i < len(d.Slide); i++ {
		// search lists
		for _, l := range d.Slide[i].List {
			for _, ll := range l.Li {
				if strings.Contains(ll, s) {
					return i
				}
			}
		}
		// search text
		for _, t := range d.Slide[i].Text {
			if strings.Contains(t.Tdata, s) {
				return i
			}
		}
	}
	return -1
}

// readcmd reads interaction commands
func readcmd(r *bufio.Reader) string {
	s, err := r.ReadBytes('\n')
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return "q"
	}
	return string(s)
}

// for every file, make a deck
func main() {
	if len(os.Args) > 1 {
		for _, f := range os.Args[1:] {
			dodeck(f)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// vtcombo: a single binary that contains:
// - a ZK topology server based on an in-memory map.
// - one vtgate instance.
// - many vttablet instaces.
package main

import (
	"flag"
	"time"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/exit"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/tabletserver"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/vtgate"
	"github.com/youtube/vitess/go/vt/vtgate/planbuilder"
	"github.com/youtube/vitess/go/vt/zktopo"
	"github.com/youtube/vitess/go/zk/fakezk"

	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

const (
	// cell is the cell to use for this fake cluster
	cell = "test"
)

var (
	topology           = flag.String("topology", "", "Define which shards exist in the test topology in the form <keyspace>/<shardrange>:<dbname>,... The dbname must be unique among all shards, since they share a MySQL instance in the test environment.")
	shardingColumnName = flag.String("sharding_column_name", "keyspace_id", "Specifies the column to use for sharding operations")
	shardingColumnType = flag.String("sharding_column_type", "", "Specifies the type of the column to use for sharding operations")
	vschema            = flag.String("vschema", "", "vschema file")
)

func init() {
	servenv.RegisterDefaultFlags()
}

func main() {
	defer exit.Recover()

	// flag parsing
	flags := dbconfigs.AppConfig | dbconfigs.DbaConfig |
		dbconfigs.FilteredConfig | dbconfigs.ReplConfig
	dbconfigs.RegisterFlags(flags)
	mysqlctl.RegisterFlags()
	flag.Parse()
	if len(flag.Args()) > 0 {
		flag.Usage()
		log.Errorf("vtcombo doesn't take any positional arguments")
		exit.Return(1)
	}

	// register topo server
	topo.RegisterServer("fakezk", zktopo.NewServer(fakezk.NewConn()))
	ts := topo.GetServerByName("fakezk")

	servenv.Init()
	tabletserver.Init()

	// database configs
	mycnf, err := mysqlctl.NewMycnfFromFlags(0)
	if err != nil {
		log.Errorf("mycnf read failed: %v", err)
		exit.Return(1)
	}
	dbcfgs, err := dbconfigs.Init(mycnf.SocketFile, flags)
	if err != nil {
		log.Warning(err)
	}
	mysqld := mysqlctl.NewMysqld("Dba", "App", mycnf, &dbcfgs.Dba, &dbcfgs.App.ConnParams, &dbcfgs.Repl)

	// tablets configuration and init
	initTabletMap(ts, *topology, mysqld, dbcfgs, mycnf)

	// vschema
	var schema *planbuilder.Schema
	if *vschema != "" {
		schema, err = planbuilder.LoadFile(*vschema)
		if err != nil {
			log.Error(err)
			exit.Return(1)
		}
		log.Infof("v3 is enabled: loaded schema from file")
	}

	// vtgate configuration and init
	resilientSrvTopoServer := vtgate.NewResilientSrvTopoServer(ts, "ResilientSrvTopoServer")
	healthCheck := discovery.NewHealthCheck(30*time.Second /*connTimeoutTotal*/, 1*time.Millisecond /*retryDelay*/)
	tabletTypesToWait := []topodatapb.TabletType{
		topodatapb.TabletType_MASTER,
		topodatapb.TabletType_REPLICA,
		topodatapb.TabletType_RDONLY,
	}
	vtgate.Init(healthCheck, ts, resilientSrvTopoServer, schema, cell, 1*time.Millisecond /*retryDelay*/, 2 /*retryCount*/, 30*time.Second /*connTimeoutTotal*/, 10*time.Second /*connTimeoutPerConn*/, 365*24*time.Hour /*connLife*/, tabletTypesToWait, 0 /*maxInFlight*/, "" /*testGateway*/)

	servenv.OnTerm(func() {
		// FIXME(alainjobart) stop vtgate, all tablets
		//		qsc.DisallowQueries()
		//		agent.Stop()
	})
	servenv.OnClose(func() {
		// We will still use the topo server during lameduck period
		// to update our state, so closing it in OnClose()
		topo.CloseServers()
	})
	servenv.RunDefault()
}
<commit_msg>Fixing default value for vtcombo's sharding column type to be consistent.<commit_after>// Copyright 2015, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// vtcombo: a single binary that contains:
// - a ZK topology server based on an in-memory map.
// - one vtgate instance.
// - many vttablet instaces.
package main

import (
	"flag"
	"time"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/exit"
	"github.com/youtube/vitess/go/vt/dbconfigs"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/mysqlctl"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/tabletserver"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/vtgate"
	"github.com/youtube/vitess/go/vt/vtgate/planbuilder"
	"github.com/youtube/vitess/go/vt/zktopo"
	"github.com/youtube/vitess/go/zk/fakezk"

	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

const (
	// cell is the cell to use for this fake cluster
	cell = "test"
)

var (
	topology           = flag.String("topology", "", "Define which shards exist in the test topology in the form <keyspace>/<shardrange>:<dbname>,... The dbname must be unique among all shards, since they share a MySQL instance in the test environment.")
	shardingColumnName = flag.String("sharding_column_name", "keyspace_id", "Specifies the column to use for sharding operations")
	shardingColumnType = flag.String("sharding_column_type", "uint64", "Specifies the type of the column to use for sharding operations")
	vschema            = flag.String("vschema", "", "vschema file")
)

func init() {
	servenv.RegisterDefaultFlags()
}

func main() {
	defer exit.Recover()

	// flag parsing
	flags := dbconfigs.AppConfig | dbconfigs.DbaConfig |
		dbconfigs.FilteredConfig | dbconfigs.ReplConfig
	dbconfigs.RegisterFlags(flags)
	mysqlctl.RegisterFlags()
	flag.Parse()
	if len(flag.Args()) > 0 {
		flag.Usage()
		log.Errorf("vtcombo doesn't take any positional arguments")
		exit.Return(1)
	}

	// register topo server
	topo.RegisterServer("fakezk", zktopo.NewServer(fakezk.NewConn()))
	ts := topo.GetServerByName("fakezk")

	servenv.Init()
	tabletserver.Init()

	// database configs
	mycnf, err := mysqlctl.NewMycnfFromFlags(0)
	if err != nil {
		log.Errorf("mycnf read failed: %v", err)
		exit.Return(1)
	}
	dbcfgs, err := dbconfigs.Init(mycnf.SocketFile, flags)
	if err != nil {
		log.Warning(err)
	}
	mysqld := mysqlctl.NewMysqld("Dba", "App", mycnf, &dbcfgs.Dba, &dbcfgs.App.ConnParams, &dbcfgs.Repl)

	// tablets configuration and init
	initTabletMap(ts, *topology, mysqld, dbcfgs, mycnf)

	// vschema
	var schema *planbuilder.Schema
	if *vschema != "" {
		schema, err = planbuilder.LoadFile(*vschema)
		if err != nil {
			log.Error(err)
			exit.Return(1)
		}
		log.Infof("v3 is enabled: loaded schema from file")
	}

	// vtgate configuration and init
	resilientSrvTopoServer := vtgate.NewResilientSrvTopoServer(ts, "ResilientSrvTopoServer")
	healthCheck := discovery.NewHealthCheck(30*time.Second /*connTimeoutTotal*/, 1*time.Millisecond /*retryDelay*/)
	tabletTypesToWait := []topodatapb.TabletType{
		topodatapb.TabletType_MASTER,
		topodatapb.TabletType_REPLICA,
		topodatapb.TabletType_RDONLY,
	}
	vtgate.Init(healthCheck, ts, resilientSrvTopoServer, schema, cell, 1*time.Millisecond /*retryDelay*/, 2 /*retryCount*/, 30*time.Second /*connTimeoutTotal*/, 10*time.Second /*connTimeoutPerConn*/, 365*24*time.Hour /*connLife*/, tabletTypesToWait, 0 /*maxInFlight*/, "" /*testGateway*/)

	servenv.OnTerm(func() {
		// FIXME(alainjobart) stop vtgate, all tablets
		//		qsc.DisallowQueries()
		//		agent.Stop()
	})
	servenv.OnClose(func() {
		// We will still use the topo server during lameduck period
		// to update our state, so closing it in OnClose()
		topo.CloseServers()
	})
	servenv.RunDefault()
}
<|endoftext|>


<commit_before>package mysqlconn

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"net"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/tb"
)

const (
	// DefaultServerVersion is the default server version we're sending to the client.
	// Can be changed.
	DefaultServerVersion = "5.5.10-Vitess"
)

// A Handler is an interface used by Listener to send queries.
// The implementation of this interface may store data in the ClientData
// field of the Connection for its own purposes.
//
// For a given Connection, all these methods are serialized. It means
// only one of these methods will be called concurrently for a given
// Connection. So access to the Connection ClientData does not need to
// be protected by a mutex.
//
// However, each connection is using one go routine, so multiple
// Connection objects can call these concurrently, for different Connections.
type Handler interface {
	// NewConnection is called when a connection is created.
	// It is not established yet. The handler can decide to
	// set StatusFlags that will be returned by the handshake methods.
	// In particular, ServerStatusAutocommit might be set.
	NewConnection(c *Conn)

	// ConnectionClosed is called when a connection is closed.
	ConnectionClosed(c *Conn)

	// ComQuery is called when a connection receives a query.
	ComQuery(c *Conn, query string) (*sqltypes.Result, error)
}

// Listener is the MySQL server protocol listener.
type Listener struct {
	// Construction parameters, set by NewListener.

	// handler is the data handler.
	handler Handler

	// This is the main listener socket.
	listener net.Listener

	// The following parameters are read by multiple connection go
	// routines.  They are not protected by a mutex, so they
	// should be set after NewListener, and not changed while
	// Accept is running.

	// ServerVersion is the version we will advertise.
	ServerVersion string

	// PasswordMap maps users to passwords.
	PasswordMap map[string]string

	// The following parameters are changed by the Accept routine.

	// Incrementing ID for connection id.
	connectionID uint32
}

// NewListener creates a new Listener.
func NewListener(protocol, address string, handler Handler) (*Listener, error) {
	listener, err := net.Listen(protocol, address)
	if err != nil {
		return nil, err
	}

	return &Listener{
		ServerVersion: DefaultServerVersion,
		handler:       handler,
		PasswordMap:   make(map[string]string),
		listener:      listener,
		connectionID:  1,
	}, nil
}

// Addr returns the listener address.
func (l *Listener) Addr() net.Addr {
	return l.listener.Addr()
}

// Accept runs an accept loop until the listener is closed.
func (l *Listener) Accept() {
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			// Close() was probably called.
			return
		}

		connectionID := l.connectionID
		l.connectionID++

		go l.handle(conn, connectionID)
	}
}

// handle is called in a go routine for each client connection.
// FIXME(alainjobart) handle per-connection logs in a way that makes sense.
// FIXME(alainjobart) add an idle timeout for the connection.
func (l *Listener) handle(conn net.Conn, connectionID uint32) {
	c := newConn(conn)
	c.ConnectionID = connectionID

	// Catch panics, and close the connection in any case.
	defer func() {
		if x := recover(); x != nil {
			log.Errorf("mysql_server caught panic:\n%v\n%s", x, tb.Stack(4))
		}
		conn.Close()
	}()

	// Tell the handler about the connection coming and going.
	l.handler.NewConnection(c)
	defer l.handler.ConnectionClosed(c)

	// First build and send the server handshake packet.
	cipher, err := c.writeHandshakeV10(l.ServerVersion)
	if err != nil {
		log.Errorf("Cannot send HandshakeV10 packet: %v", err)
		return
	}

	// Wait for the client response.
	response, err := c.readEphemeralPacket()
	if err != nil {
		log.Errorf("Cannot read client handshake response: %v", err)
		return
	}
	username, authResponse, err := l.parseClientHandshakePacket(c, response)
	if err != nil {
		log.Errorf("Cannot parse client handshake response: %v", err)
		return
	}

	// Find the user in our map
	password, ok := l.PasswordMap[username]
	if !ok {
		log.Errorf("Invalid user: %v", username)
		c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
		return
	}

	// Validate the password.
	computedAuthResponse := scramblePassword(cipher, []byte(password))
	if bytes.Compare(authResponse, computedAuthResponse) != 0 {
		log.Errorf("Invalid password for user %v", username)
		c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
		return
	}

	// Send an OK packet.
	if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
		log.Errorf("Cannot write OK packet: %v", err)
		return
	}

	for {
		c.sequence = 0
		data, err := c.readEphemeralPacket()
		if err != nil {
			log.Errorf("Error reading packet from client %v: %v", c.ConnectionID, err)
			return
		}

		switch data[0] {
		case ComQuit:
			log.Infof("Client %v wants to disconnect, closing connection.", c.ConnectionID)
			return
		case ComInitDB:
			db := c.parseComInitDB(data)
			c.SchemaName = db
			if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
				log.Errorf("Error writing ComInitDB result to client %v: %v", c.ConnectionID, err)
				return
			}
		case ComQuery:
			query := c.parseComQuery(data)
			log.Infof("Received command from client %v: %v", c.ConnectionID, query)
			result, err := l.handler.ComQuery(c, query)
			if err != nil {
				if werr := c.writeErrorPacketFromError(err); werr != nil {
					// If we can't even write the error, we're done.
					log.Errorf("Error writing query error to client %v: %v", c.ConnectionID, werr)
					return
				}
				continue
			}
			if err := c.writeResult(result); err != nil {
				log.Errorf("Error writing result to client %v: %v", c.ConnectionID, err)
				return
			}
		case ComPing:
			// No payload to that one, just return OKPacket.
			if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
				log.Errorf("Error writing ComPing result to client %v: %v", c.ConnectionID, err)
				return
			}
		default:
			log.Errorf("Got unhandled packet from client %v, returning error: %v", c.ConnectionID, data)
			if err := c.writeErrorPacket(ERUnknownComError, SSUnknownComError, "command handling not implemented yet: %v", data[0]); err != nil {
				log.Errorf("Error writing error packet to client: %v", err)
				return
			}

		}
	}
}

// Close stops the listener, and closes all connections.
func (l *Listener) Close() {
	l.listener.Close()
}

// writeHandshakeV10 writes the Initial Handshake Packet, server side.
// It returns the cipher data.
func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
	capabilities := CapabilityClientLongPassword |
		CapabilityClientLongFlag |
		CapabilityClientConnectWithDB |
		CapabilityClientProtocol41 |
		CapabilityClientTransactions |
		CapabilityClientSecureConnection |
		CapabilityClientPluginAuth |
		CapabilityClientPluginAuthLenencClientData |
		CapabilityClientDeprecateEOF

	length :=
		1 + // protocol version
			lenNullString(serverVersion) +
			4 + // connection ID
			8 + // first part of cipher data
			1 + // filler byte
			2 + // capability flags (lower 2 bytes)
			1 + // character set
			2 + // status flag
			2 + // capability flags (upper 2 bytes)
			1 + // length of auth plugin data
			10 + // reserved (0)
			13 + // auth-plugin-data
			lenNullString(mysqlNativePassword) // auth-plugin-name

	data := c.startEphemeralPacket(length)
	pos := 0

	// Protocol version.
	pos = writeByte(data, pos, protocolVersion)

	// Copy server version.
	pos = writeNullString(data, pos, serverVersion)

	// Add connectionID in.
	pos = writeUint32(data, pos, c.ConnectionID)

	// Generate the cipher, put 8 bytes in.
	cipher := make([]byte, 20)
	if _, err := rand.Read(cipher); err != nil {
		return nil, err
	}
	pos += copy(data[pos:], cipher[:8])

	// One filler byte, always 0.
	pos = writeByte(data, pos, 0)

	// Lower part of the capability flags.
	pos = writeUint16(data, pos, uint16(capabilities))

	// Character set.
	pos = writeByte(data, pos, CharacterSetUtf8)

	// Status flag.
	pos = writeUint16(data, pos, c.StatusFlags)

	// Upper part of the capability flags.
	pos = writeUint16(data, pos, uint16(capabilities>>16))

	// Length of auth plugin data.
	// Always 21 (8 + 13).
	pos = writeByte(data, pos, 21)

	// Reserved
	pos += 10

	// Second part of auth plugin data.
	pos += copy(data[pos:], cipher[8:])
	data[pos] = 0
	pos++

	// Copy authPluginName.
	pos = writeNullString(data, pos, mysqlNativePassword)

	// Sanity check.
	if pos != len(data) {
		return nil, fmt.Errorf("error building Handshake packet: got %v bytes expected %v", pos, len(data))
	}

	if err := c.writeEphemeralPacket(true); err != nil {
		return nil, err
	}

	return cipher, nil
}

// parseClientHandshakePacket parses the handshake sent by the client.
// Returns the username, auth-data, error.
func (l *Listener) parseClientHandshakePacket(c *Conn, data []byte) (string, []byte, error) {
	pos := 0

	// Client flags, 4 bytes.
	clientFlags, pos, ok := readUint32(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read client flags")
	}
	if clientFlags&CapabilityClientProtocol41 == 0 {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: only support protocol 4.1")
	}

	// Remember a subset of the capabilities, so we can use them later in the protocol.
	c.Capabilities = clientFlags & (CapabilityClientDeprecateEOF)

	// Max packet size. Don't do anything with this now.
	// See doc.go for more information.
	/*maxPacketSize*/
	_, pos, ok = readUint32(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read maxPacketSize")
	}

	// Character set. Need to handle it.
	characterSet, pos, ok := readByte(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read characterSet")
	}
	c.CharacterSet = characterSet

	// 23x reserved zero bytes.
	pos += 23

	// username
	username, pos, ok := readNullString(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read username")
	}

	// auth-response can have three forms.
	var authResponse []byte
	if clientFlags&CapabilityClientPluginAuthLenencClientData != 0 {
		var l uint64
		l, pos, ok = readLenEncInt(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response variable length")
		}
		authResponse, pos, ok = readBytes(data, pos, int(l))
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}

	} else if clientFlags&CapabilityClientSecureConnection != 0 {
		var l byte
		l, pos, ok = readByte(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response length")
		}

		authResponse, pos, ok = readBytes(data, pos, int(l))
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}
	} else {
		a := ""
		a, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}
		authResponse = []byte(a)
	}

	// db name.
	if clientFlags&CapabilityClientConnectWithDB != 0 {
		dbname := ""
		dbname, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read dbname")
		}
		c.SchemaName = dbname
	}

	// auth plugin name
	authPluginName := "mysql_native_password"
	if clientFlags&CapabilityClientPluginAuth != 0 {
		authPluginName, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read authPluginName")
		}
	}
	if authPluginName != mysqlNativePassword {
		return "", nil, fmt.Errorf("invalid authPluginName, got %v but only support %v", authPluginName, mysqlNativePassword)
	}

	// FIXME(alainjobart) Add CLIENT_CONNECT_ATTRS parsing if we need it.

	return username, authResponse, nil
}
<commit_msg>Cipher must be a legal UTF8 string.<commit_after>package mysqlconn

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"net"

	log "github.com/golang/glog"
	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/tb"
)

const (
	// DefaultServerVersion is the default server version we're sending to the client.
	// Can be changed.
	DefaultServerVersion = "5.5.10-Vitess"
)

// A Handler is an interface used by Listener to send queries.
// The implementation of this interface may store data in the ClientData
// field of the Connection for its own purposes.
//
// For a given Connection, all these methods are serialized. It means
// only one of these methods will be called concurrently for a given
// Connection. So access to the Connection ClientData does not need to
// be protected by a mutex.
//
// However, each connection is using one go routine, so multiple
// Connection objects can call these concurrently, for different Connections.
type Handler interface {
	// NewConnection is called when a connection is created.
	// It is not established yet. The handler can decide to
	// set StatusFlags that will be returned by the handshake methods.
	// In particular, ServerStatusAutocommit might be set.
	NewConnection(c *Conn)

	// ConnectionClosed is called when a connection is closed.
	ConnectionClosed(c *Conn)

	// ComQuery is called when a connection receives a query.
	ComQuery(c *Conn, query string) (*sqltypes.Result, error)
}

// Listener is the MySQL server protocol listener.
type Listener struct {
	// Construction parameters, set by NewListener.

	// handler is the data handler.
	handler Handler

	// This is the main listener socket.
	listener net.Listener

	// The following parameters are read by multiple connection go
	// routines.  They are not protected by a mutex, so they
	// should be set after NewListener, and not changed while
	// Accept is running.

	// ServerVersion is the version we will advertise.
	ServerVersion string

	// PasswordMap maps users to passwords.
	PasswordMap map[string]string

	// The following parameters are changed by the Accept routine.

	// Incrementing ID for connection id.
	connectionID uint32
}

// NewListener creates a new Listener.
func NewListener(protocol, address string, handler Handler) (*Listener, error) {
	listener, err := net.Listen(protocol, address)
	if err != nil {
		return nil, err
	}

	return &Listener{
		ServerVersion: DefaultServerVersion,
		handler:       handler,
		PasswordMap:   make(map[string]string),
		listener:      listener,
		connectionID:  1,
	}, nil
}

// Addr returns the listener address.
func (l *Listener) Addr() net.Addr {
	return l.listener.Addr()
}

// Accept runs an accept loop until the listener is closed.
func (l *Listener) Accept() {
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			// Close() was probably called.
			return
		}

		connectionID := l.connectionID
		l.connectionID++

		go l.handle(conn, connectionID)
	}
}

// handle is called in a go routine for each client connection.
// FIXME(alainjobart) handle per-connection logs in a way that makes sense.
// FIXME(alainjobart) add an idle timeout for the connection.
func (l *Listener) handle(conn net.Conn, connectionID uint32) {
	c := newConn(conn)
	c.ConnectionID = connectionID

	// Catch panics, and close the connection in any case.
	defer func() {
		if x := recover(); x != nil {
			log.Errorf("mysql_server caught panic:\n%v\n%s", x, tb.Stack(4))
		}
		conn.Close()
	}()

	// Tell the handler about the connection coming and going.
	l.handler.NewConnection(c)
	defer l.handler.ConnectionClosed(c)

	// First build and send the server handshake packet.
	cipher, err := c.writeHandshakeV10(l.ServerVersion)
	if err != nil {
		log.Errorf("Cannot send HandshakeV10 packet: %v", err)
		return
	}

	// Wait for the client response.
	response, err := c.readEphemeralPacket()
	if err != nil {
		log.Errorf("Cannot read client handshake response: %v", err)
		return
	}
	username, authResponse, err := l.parseClientHandshakePacket(c, response)
	if err != nil {
		log.Errorf("Cannot parse client handshake response: %v", err)
		return
	}

	// Find the user in our map
	password, ok := l.PasswordMap[username]
	if !ok {
		log.Errorf("Invalid user: %v", username)
		c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
		return
	}

	// Validate the password.
	computedAuthResponse := scramblePassword(cipher, []byte(password))
	if bytes.Compare(authResponse, computedAuthResponse) != 0 {
		log.Errorf("Invalid password for user %v", username)
		c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
		return
	}

	// Send an OK packet.
	if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
		log.Errorf("Cannot write OK packet: %v", err)
		return
	}

	for {
		c.sequence = 0
		data, err := c.readEphemeralPacket()
		if err != nil {
			log.Errorf("Error reading packet from client %v: %v", c.ConnectionID, err)
			return
		}

		switch data[0] {
		case ComQuit:
			log.Infof("Client %v wants to disconnect, closing connection.", c.ConnectionID)
			return
		case ComInitDB:
			db := c.parseComInitDB(data)
			c.SchemaName = db
			if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
				log.Errorf("Error writing ComInitDB result to client %v: %v", c.ConnectionID, err)
				return
			}
		case ComQuery:
			query := c.parseComQuery(data)
			log.Infof("Received command from client %v: %v", c.ConnectionID, query)
			result, err := l.handler.ComQuery(c, query)
			if err != nil {
				if werr := c.writeErrorPacketFromError(err); werr != nil {
					// If we can't even write the error, we're done.
					log.Errorf("Error writing query error to client %v: %v", c.ConnectionID, werr)
					return
				}
				continue
			}
			if err := c.writeResult(result); err != nil {
				log.Errorf("Error writing result to client %v: %v", c.ConnectionID, err)
				return
			}
		case ComPing:
			// No payload to that one, just return OKPacket.
			if err := c.writeOKPacket(0, 0, c.StatusFlags, 0); err != nil {
				log.Errorf("Error writing ComPing result to client %v: %v", c.ConnectionID, err)
				return
			}
		default:
			log.Errorf("Got unhandled packet from client %v, returning error: %v", c.ConnectionID, data)
			if err := c.writeErrorPacket(ERUnknownComError, SSUnknownComError, "command handling not implemented yet: %v", data[0]); err != nil {
				log.Errorf("Error writing error packet to client: %v", err)
				return
			}

		}
	}
}

// Close stops the listener, and closes all connections.
func (l *Listener) Close() {
	l.listener.Close()
}

// writeHandshakeV10 writes the Initial Handshake Packet, server side.
// It returns the cipher data.
func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
	capabilities := CapabilityClientLongPassword |
		CapabilityClientLongFlag |
		CapabilityClientConnectWithDB |
		CapabilityClientProtocol41 |
		CapabilityClientTransactions |
		CapabilityClientSecureConnection |
		CapabilityClientPluginAuth |
		CapabilityClientPluginAuthLenencClientData |
		CapabilityClientDeprecateEOF

	length :=
		1 + // protocol version
			lenNullString(serverVersion) +
			4 + // connection ID
			8 + // first part of cipher data
			1 + // filler byte
			2 + // capability flags (lower 2 bytes)
			1 + // character set
			2 + // status flag
			2 + // capability flags (upper 2 bytes)
			1 + // length of auth plugin data
			10 + // reserved (0)
			13 + // auth-plugin-data
			lenNullString(mysqlNativePassword) // auth-plugin-name

	data := c.startEphemeralPacket(length)
	pos := 0

	// Protocol version.
	pos = writeByte(data, pos, protocolVersion)

	// Copy server version.
	pos = writeNullString(data, pos, serverVersion)

	// Add connectionID in.
	pos = writeUint32(data, pos, c.ConnectionID)

	// Generate the cipher, put 8 bytes in.
	cipher := make([]byte, 20)
	if _, err := rand.Read(cipher); err != nil {
		return nil, err
	}

        // Cipher must be a legal UTF8 string.
	for i := 0; i < len(cipher); i++ {
		cipher[i] &= 0x7f
		if cipher[i] == '\x00' || cipher[i] == '$' {
			cipher[i] += 1
		}
	}
	pos += copy(data[pos:], cipher[:8])

	// One filler byte, always 0.
	pos = writeByte(data, pos, 0)

	// Lower part of the capability flags.
	pos = writeUint16(data, pos, uint16(capabilities))

	// Character set.
	pos = writeByte(data, pos, CharacterSetUtf8)

	// Status flag.
	pos = writeUint16(data, pos, c.StatusFlags)

	// Upper part of the capability flags.
	pos = writeUint16(data, pos, uint16(capabilities>>16))

	// Length of auth plugin data.
	// Always 21 (8 + 13).
	pos = writeByte(data, pos, 21)

	// Reserved
	pos += 10

	// Second part of auth plugin data.
	pos += copy(data[pos:], cipher[8:])
	data[pos] = 0
	pos++

	// Copy authPluginName.
	pos = writeNullString(data, pos, mysqlNativePassword)

	// Sanity check.
	if pos != len(data) {
		return nil, fmt.Errorf("error building Handshake packet: got %v bytes expected %v", pos, len(data))
	}

	if err := c.writeEphemeralPacket(true); err != nil {
		return nil, err
	}

	return cipher, nil
}

// parseClientHandshakePacket parses the handshake sent by the client.
// Returns the username, auth-data, error.
func (l *Listener) parseClientHandshakePacket(c *Conn, data []byte) (string, []byte, error) {
	pos := 0

	// Client flags, 4 bytes.
	clientFlags, pos, ok := readUint32(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read client flags")
	}
	if clientFlags&CapabilityClientProtocol41 == 0 {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: only support protocol 4.1")
	}

	// Remember a subset of the capabilities, so we can use them later in the protocol.
	c.Capabilities = clientFlags & (CapabilityClientDeprecateEOF)

	// Max packet size. Don't do anything with this now.
	// See doc.go for more information.
	/*maxPacketSize*/
	_, pos, ok = readUint32(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read maxPacketSize")
	}

	// Character set. Need to handle it.
	characterSet, pos, ok := readByte(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read characterSet")
	}
	c.CharacterSet = characterSet

	// 23x reserved zero bytes.
	pos += 23

	// username
	username, pos, ok := readNullString(data, pos)
	if !ok {
		return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read username")
	}

	// auth-response can have three forms.
	var authResponse []byte
	if clientFlags&CapabilityClientPluginAuthLenencClientData != 0 {
		var l uint64
		l, pos, ok = readLenEncInt(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response variable length")
		}
		authResponse, pos, ok = readBytes(data, pos, int(l))
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}

	} else if clientFlags&CapabilityClientSecureConnection != 0 {
		var l byte
		l, pos, ok = readByte(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response length")
		}

		authResponse, pos, ok = readBytes(data, pos, int(l))
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}
	} else {
		a := ""
		a, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
		}
		authResponse = []byte(a)
	}

	// db name.
	if clientFlags&CapabilityClientConnectWithDB != 0 {
		dbname := ""
		dbname, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read dbname")
		}
		c.SchemaName = dbname
	}

	// auth plugin name
	authPluginName := "mysql_native_password"
	if clientFlags&CapabilityClientPluginAuth != 0 {
		authPluginName, pos, ok = readNullString(data, pos)
		if !ok {
			return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read authPluginName")
		}
	}
	if authPluginName != mysqlNativePassword {
		return "", nil, fmt.Errorf("invalid authPluginName, got %v but only support %v", authPluginName, mysqlNativePassword)
	}

	// FIXME(alainjobart) Add CLIENT_CONNECT_ATTRS parsing if we need it.

	return username, authResponse, nil
}
<|endoftext|>


<commit_before>package auth

/*
 * Implements auth middleware for cention applications.
 * Expects to be running in Gin framework
 */

import (
	wf "c3/osm/webframework"
	"c3/osm/workflow"
	"c3/web/controllers"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/cention-mujibur-rahman/gobcache"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/securecookie"
)

const (
	HTTP_UNAUTHORIZE_ACCESS = 401
	HTTP_FORBIDDEN_ACCESS   = 403
	HTTP_MOVED_PERMANENTLY  = 301
)

var (
	hashKey                        = securecookie.GenerateRandomKey(64)
	blockKey                       = securecookie.GenerateRandomKey(32)
	sc                             = securecookie.New(hashKey, blockKey)
	ERROR_USER_PASS_MISMATCH       = errors.New("Username or Password doesnt match!")
	ERROR_CACHE_MISSED             = errors.New("Cache missed somehow!")
	ERROR_ON_SECURECOOKIE_HASHKEY  = errors.New("HashKey doesn't match with secureCookie")
	ERROR_COOKIE_NOT_FOUND         = errors.New("Cookie: cention-suiteSSID=? not found")
	ERROR_WF_USER_NULL             = errors.New("webframework user is null")
	ERROR_ON_MULTIPLE_LOGIN_COOKIE = errors.New("User logged in from multiple place")
)

func checkingMemcache() bool {
	conn, err := net.Dial("tcp", "localhost:11211")
	if err != nil {
		log.Println("Memcache is not running! ", err)
		return false
	}
	defer conn.Close()
	return true
}
func getCookieHashKey(ctx *gin.Context) (string, error) {
	cookie, err := ctx.Request.Cookie("cention-suiteSSID")
	if err != nil {
		log.Println("getCookieHashKey(): Cookie is empty - ", err)
		return "", err
	}
	if cookie != nil && cookie.Value == "" && cookie.Value == "guest" {
		return "", ERROR_CACHE_MISSED
	}
	return cookie.Value, nil
}

func decodeCookie(ctx *gin.Context) (bool, string, error) {
	cookie, err := getCookieHashKey(ctx)
	if err != nil {
		return false, "", err
	}
	if cookie != "" && cookie != "guest" {
		value := make(map[string]string)
		if err = sc.Decode("cention-suiteSSID", cookie, &value); err == nil {
			log.Println("Cookie[`cention-suiteSSID`]", value["username"])
			return true, value["username"], nil
		} else {
			//Somehow cookie got malformed so need to invalidate
			log.Println("decodeCookie(): Error to decode ", err)
			InvalidateCookie(ctx)
			return false, "", err
		}
	} else {
		return false, "", err
	}
	return false, "", ERROR_COOKIE_NOT_FOUND
}
func validateByOnlyCookie(ssid string) (*wf.User, error) {
	wu, err := wf.QueryUser_byHashLogin(ssid)
	if err != nil {
		log.Println("validateByOnlyCookie(): Error on loading - wf.QueryUser_byLogin", err)
		return nil, err
	}
	if wu != nil {
		if wu.Active {
			return wu, nil
		}
	}
	return nil, ERROR_ON_SECURECOOKIE_HASHKEY

}
func InvalidateCookie(ctx *gin.Context) {
	ctx.Writer.Header().Add("Set-Cookie", "cention-suiteSSID=guest; Path=/;MaxAge=-1")
}
func CheckOrCreateAuthCookie(ctx *gin.Context) error {
	user := ctx.Request.FormValue("username")
	pass := ctx.Request.FormValue("password")
	mu := &sync.Mutex{}
	if ok, cookieUser, err := decodeCookie(ctx); err == nil && ok {
		ssid, _ := getCookieHashKey(ctx)
		cu, err := validateByOnlyCookie(ssid)
		if err != nil {
			log.Println("Error on CheckOrCreateAuthCookie() - validateByOnlyCookie(): ", err)
			return err
		}
		if cu != nil {
			log.Printf("CentionAuth: User `%s` has logedin now", cookieUser)
			return nil
		} else {
			log.Println("Error on CheckOrCreateAuthCookie() - validateByOnlyCookie(): ", err)
			InvalidateCookie(ctx)
			return ERROR_ON_MULTIPLE_LOGIN_COOKIE
		}
	} else if user == "" && pass == "" {
		//log.Println("Error on empty user/pass")
		return ERROR_USER_PASS_MISMATCH
	} else {
		expires := time.Now().Add(time.Minute * 135)
		wfUser, err := validateUser(user, pass)
		if err != nil {
			log.Println("Error on CheckOrCreateAuthCookie() - validateUser: ", err)
			return err
		}
		if wfUser != nil {
			value := map[string]string{
				"username": user,
			}
			mu.Lock()
			if encoded, err := sc.Encode("cention-suiteSSID", value); err == nil {
				wfUser.SetSsid(encoded)
				if err := wfUser.Save(); err != nil {
					log.Println("CheckOrCreateAuthCookie(): Error on saving webframework User: ", err)
				}
				//Saving the cookie to memcache
				if checkingMemcache() {
					if err = gobcache.SaveInMemcache("Session_"+encoded, wfUser.Id); err != nil {
						log.Println("[`SaveInMemcache`] Error on saving:", err)
					}
				}
				log.Printf("CentionAuth: User `%s` just now Logged In", wfUser.Username)
				cookie := fmt.Sprintf("cention-suiteSSID=%s; Path=/;expires=%s", encoded, expires)
				ctx.Writer.Header().Add("Set-Cookie", cookie)
				defer mu.Unlock()
				return nil
			} else {
				log.Println("CheckOrCreateAuthCookie(): Error on wrong cookie - ", err)
				return err
			}
		}
		return ERROR_USER_PASS_MISMATCH
	}
}
func CheckAuthCookie(ctx *gin.Context) error {
	ssid, err := getCookieHashKey(ctx)
	if err != nil {
		log.Println("Error on CheckAuthCookie() - getCookieHashKey(): ", err)
		return err
	}
	cu, err := validateByOnlyCookie(ssid)
	if err != nil {
		return err
	}
	if cu == nil {
		log.Println("Error on CheckAuthCookie() - Webframework user nil: ")
		return ERROR_ON_MULTIPLE_LOGIN_COOKIE
	}
	return nil
}
func validateUser(user, pass string) (*wf.User, error) {
	if user != "" && pass != "" {
		wu, err := wf.QueryUser_byLogin(user)
		if err != nil {
			log.Println("validateUser() - Error on loading: wf.QueryUser_byLogin", err)
			return nil, err
		}
		if wu != nil {
			if wu.Active && encodePassword(pass) == wu.Password {
				return wu, nil
			}
			log.Println("Either Cookie or (Username and Password) doesnt match  or User not active")
			return nil, nil
		}
	}
	msg := "!! Provided wrong Username or Password"
	if user == "" || pass == "" {
		msg = "!! Provided empty Username or Password."
	}
	log.Println(msg)
	return nil, ERROR_USER_PASS_MISMATCH
}
func encodePassword(p string) string {
	ep := sha256.New()
	_, err := ep.Write([]byte(p))
	if err != nil {
		log.Println("encodePassword() - Error on Sha256: ", err)
	}
	return base64.StdEncoding.EncodeToString(ep.Sum(nil))
}
func validateUserByCookie(secureCookieUser string, ctx *gin.Context) (*wf.User, error) {
	if cookie, err := getCookieHashKey(ctx); err == nil {
		wu, err := wf.QueryUser_byHashLogin(cookie)
		if err != nil {
			log.Println("validateUserByCookie() - Error on loading: wf.QueryUser_byLogin", err)
			return nil, err
		}
		if wu != nil {
			if wu.Username == secureCookieUser { //Need to make sure the cookie hash and secureCookie name is same
				return wu, nil
			}
		}
		return nil, ERROR_ON_SECURECOOKIE_HASHKEY
	}
	return nil, ERROR_WF_USER_NULL
}
func destroyAuthCookie(ctx *gin.Context) error {
	if ok, username, err := decodeCookie(ctx); err == nil && ok {
		wfUser, err := validateUserByCookie(username, ctx)
		if err != nil {
			log.Println("destroyAuthCookie() - Error on validateUserByCookie: ", err)
		}
		if wfUser != nil && wfUser.Active {
			wfUser.SetSsid("")
			if err := wfUser.Save(); err != nil {
				log.Println("destroyAuthCookie() - Error on saving webframework User: ", err)
			}
			ssid, _ := getCookieHashKey(ctx) //Error ignored!
			if checkingMemcache() {
				gobcache.DeleteFromMemcache("Session_" + ssid)
			}
			log.Printf("User `%s` just now Logged Out", wfUser.Username)
			ctx.Writer.Header().Add("Set-Cookie", "cention-suiteSSID=guest; Path=/;MaxAge=-1")
		}
		return nil
	} else {
		log.Println("destroyAuthCookie() - Error to decode ", err)
		return err
	}
}
func Logout(ctx *gin.Context) error {
	return destroyAuthCookie(ctx)
}
func Middleware(pathPrefix string) func(*gin.Context) {
	return func(ctx *gin.Context) {
		if strings.HasPrefix(ctx.Request.RequestURI, "/debug/pprof/") {
			ctx.Next()
			return
		}
		var currUser *workflow.User
		ssid, err := getCookieHashKey(ctx)
		if err != nil {
			log.Println("Middleware() - getCookieHashKey(): Error ", err)
			ctx.AbortWithStatus(HTTP_FORBIDDEN_ACCESS)
			ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
		}
		var id int = -1
		var wfUser *wf.User
		if checkingMemcache() {
			if err = gobcache.GetFromMemcache("Session_"+ssid, &id); err != nil {
				log.Println("[Set(Cookie)] Memcache key `Session` is empty!", err)
			}
			if id != -1 {
				currUser = controllers.FetchUserObject(id)
				wfUser, err = wf.QueryUser_byHashLogin(ssid)
				if err != nil {
					log.Println("Middleware() - QueryUser_byHashLogin(): Cookie not found in Memcache: ", err)
					ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
					ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
				}
			} else {
				wfUser, err = wf.QueryUser_byHashLogin(ssid)
				if err != nil {
					log.Println("Middleware() - QueryUser_byHashLogin(): Cookie not matched in database: ", err)
					ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
					ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
				}
				if wfUser != nil {
					if wfUser.Active {
						if err = gobcache.SaveInMemcache("Session_"+ssid, wfUser.Id); err != nil {
							log.Println("[`SaveInMemcache`] Error on saving:", err)
						}
						currUser = controllers.FetchUserObject(wfUser.Id)
					} else {
						log.Println("Middleware() - Creating session - Webframework user is not active, User Id: ", wfUser.Id)
						ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
						ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
					}
				} else {
					log.Println("Middleware() - Creating session - Webframework user is nil ")
					ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
					ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
				}
			}
		} else {
			wfUser, err = wf.QueryUser_byHashLogin(ssid)
			if err != nil {
				log.Println("Middleware() - QueryUser_byHashLogin(): ", err)
				ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
				ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
			}

			if wfUser != nil {
				if wfUser.Active {
					currUser = controllers.FetchUserObject(wfUser.Id)
				} else {
					log.Println("Middleware() - Webframework user is not active, User Id: ", wfUser.Id)
					ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
					ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
				}

			} else {
				log.Println("Middleware() - Webframework user is nil")
				/*Resolve from Headers were already written. Wanted to override status code 301 with 200*/
				ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
				ctx.Redirect(HTTP_MOVED_PERMANENTLY, pathPrefix+"login")
			}
		}

		ctx.Keys = make(map[string]interface{})
		ctx.Keys["loggedInUser"] = currUser
		ctx.Keys["wfUser"] = wfUser
		ctx.Next()
	}
}
<commit_msg>Login/Logout session save to memcache and some fixes<commit_after>package auth

/*
 * Implements auth middleware for cention applications.
 * Expects to be running in Gin framework
 */

import (
	"c3/ferite"
	"c3/ferite/serialize"
	wf "c3/osm/webframework"
	"c3/web/controllers"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gobcache"
	"github.com/gorilla/securecookie"
)

const (
	HTTP_UNAUTHORIZE_ACCESS = 401
	HTTP_FORBIDDEN_ACCESS   = 403
	HTTP_FOUND              = 302
)

var (
	hashKey                       = securecookie.GenerateRandomKey(64)
	blockKey                      = securecookie.GenerateRandomKey(32)
	sc                            = securecookie.New(hashKey, blockKey)
	ERROR_USER_PASS_MISMATCH      = errors.New("Username or Password doesnt match!")
	ERROR_USER_PASS_EMPTY         = errors.New("Username or Password is empty!")
	ERROR_CACHE_MISSED            = errors.New("Cache missed somehow!")
	ERROR_ON_SECURECOOKIE_HASHKEY = errors.New("HashKey doesn't match with secureCookie")
	ERROR_COOKIE_NOT_FOUND        = errors.New("Cookie: cention-suiteSSID=? not found")
	ERROR_WF_USER_NULL            = errors.New("webframework user is null")
	ERROR_MEMCACHE_FAILED         = errors.New("Memcache not running")
)

type AuthCookieManager struct {
	UserId        int
	LastLoginTime int64
	LoggedIn      bool
}

func checkingMemcache() bool {
	conn, err := net.Dial("tcp", "localhost:11211")
	if err != nil {
		log.Println("Memcache is not running! ", err)
		return false
	}
	defer conn.Close()
	return true
}
func getCookieHashKey(ctx *gin.Context) (string, error) {
	cookie, err := ctx.Request.Cookie("cention-suiteSSID")
	if err != nil {
		log.Println("getCookieHashKey(): Cookie is empty - ", err)
		return "", err
	}
	if cookie != nil && cookie.Value == "" && cookie.Value == "guest" {
		return "", ERROR_CACHE_MISSED
	}
	return cookie.Value, nil
}

func decodeCookie(ctx *gin.Context) (string, error) {
	cookie, err := getCookieHashKey(ctx)
	if err != nil {
		return "", err
	}
	if cookie != "" && cookie != "guest" {
		return cookie, nil
		//TODO Mujibur: Do we need to verify the cookie? Not sure? Why?
		// Because if somehow webserver restarted the securecookie got vanish from memory
		// So its tried to create new. :)
		//		value := make(map[string]interface{})
		//		if err = sc.Decode("cention-suiteSSID", cookie, &value); err == nil {
		//			log.Printf("Cookie[`cention-suiteSSID`: %v] and params %v", cookie, value)
		//			return nil
		//		}
	}
	return "", ERROR_COOKIE_NOT_FOUND
}

func CheckOrCreateAuthCookie(ctx *gin.Context) error {
	ssid, err := decodeCookie(ctx)
	if err != nil {
		err := createNewAuthCookie(ctx)
		if err != nil {
			return err
		}
		return nil
	}
	user := ctx.Request.FormValue("username")
	pass := ctx.Request.FormValue("password")
	if user == "" && pass == "" {
		acm := new(AuthCookieManager)
		if checkingMemcache() {
			if err := gobcache.GetFromMemcache("Session_"+ssid, &acm); err != nil {
				log.Println("[GetFromMemcache] key `Session` is empty!")
				return err
			}
			if acm.LoggedIn && acm.UserId != 0 {
				return nil
			}
		}
		return ERROR_USER_PASS_EMPTY
	} else {
		log.Println("!!-- Seiting cookie informations to memcache. First time login.")
		wfUser, err := validateUser(user, pass)
		if err != nil {
			log.Println("Error on CheckOrCreateAuthCookie() - validateUser: ", err)
			return err
		}
		if wfUser != nil {
			lastLoginTime := time.Now().Unix()
			acm := new(AuthCookieManager)
			acm.UserId = wfUser.Id
			acm.LoggedIn = true
			acm.LastLoginTime = lastLoginTime
			if checkingMemcache() {
				if err = gobcache.SaveInMemcache("Session_"+ssid, acm); err != nil {
					log.Println("[`SaveInMemcache`] Error on saving:", err)
					return err
				}
				log.Printf("CentionAuth: User `%s` just now Logged In", wfUser.Username)
				return nil
			} else {
				return ERROR_MEMCACHE_FAILED
			}
		}
	}
	return ERROR_WF_USER_NULL
}

func validateUser(user, pass string) (*wf.User, error) {
	wu, err := wf.QueryUser_byLogin(user)
	if err != nil {
		return nil, err
	}
	if wu != nil {
		if wu.Active && wu.Password == encodePassword(pass) {
			return wu, nil
		}
	}
	return nil, ERROR_USER_PASS_MISMATCH
}
func encodePassword(p string) string {
	ep := sha256.New()
	_, err := ep.Write([]byte(p))
	if err != nil {
		log.Println("encodePassword() - Error on Sha256: ", err)
		return ""
	}
	return base64.StdEncoding.EncodeToString(ep.Sum(nil))
}
func createNewAuthCookie(ctx *gin.Context) error {
	value := map[string]interface{}{
		"cookie-set-date": time.Now().Unix(),
	}
	encoded, err := sc.Encode("cention-suiteSSID", value)
	if err != nil {
		log.Printf("createNewAuthCookie(): Error %v, creating `guest` cookie", err)
		cookie := fmt.Sprintf("cention-suiteSSID=%s; Path=/", "guest")
		ctx.Writer.Header().Add("Set-Cookie", cookie)
		return err
	}
	cookie := fmt.Sprintf("cention-suiteSSID=%s; Path=/", encoded)
	ctx.Writer.Header().Add("Set-Cookie", cookie)
	return nil
}
func CheckAuthCookie(ctx *gin.Context) (bool, error) {
	cookie, err := decodeCookie(ctx)
	if err != nil {
		return false, err
	}
	//	acm := &AuthCookieManager{
	//		UserId:   2,
	//		LoggedIn: true,
	//	}
	//	s, err := serialize.ToNative("SSID", acm)
	//	tests := struct {
	//		name string
	//		list interface{}
	//		want string
	//	}{
	//		name: "integer number",
	//		list: 42,
	//	}
	ss := ferite.NewArray(2, 559922377, true)
	s, err := serialize.ToNative("SSID", ss)
	if err != nil {
		log.Println("Error:", err)
	}
	log.Println("FFSession_" + cookie)
	err = gobcache.SetRawToMemcache("FFSession_"+cookie, interface{}(s))
	if err != nil {
		log.Println("Errorw:", err)
	}
	ok, err := validateByBrowserCookie(cookie)
	if err != nil {
		return false, err
	}
	if !ok {
		return false, err
	}
	return true, nil
}

func validateByBrowserCookie(ssid string) (bool, error) {
	acm := new(AuthCookieManager)
	if checkingMemcache() {
		if err := gobcache.GetFromMemcache("Session_"+ssid, &acm); err != nil {
			log.Println("[GetFromMemcache] key `Session` is empty!")
			return false, err
		}
		if acm.LoggedIn && acm.UserId != 0 {
			log.Println("Time after", acm.LastLoginTime)
			if err := updateTimeStampToMemcacheSSID(ssid, acm.UserId, true); err != nil {
				log.Printf("Error on validateByBrowserCookie(): %v", err)
				return false, err
			}
			log.Println("Time after")
			log.Printf("CentionAuth: Cookie has vrified with this info: %v", acm)
			return true, nil
		}
		log.Printf("CentionAuth redirect to login: Cookie info: %v", acm)
		return false, nil
	}
	return false, ERROR_MEMCACHE_FAILED
	/*
		//TODO Mujibur: I wrote and kept intentionally for future :P
		// Please dont argue with that. because wanting to get rif of from database while logging in and session checks
		wfv, err := wf.QueryVoucher_byHashLogin(ssid)
		if err != nil {
			log.Println("validateByOnlyCookie(): Error on loading - wf.QueryVoucher_byHashLogin", err)
			return false, err
		}
		if wfv != nil {
			if wfv.Loggedin && wfv.Active {
				return true, nil
			}
		}
		//Because wfv null is not an error.
		return false, nil
	*/
}

//Not used now.
//Kept that for future if anyhow is needed
func validateUserByCookie(secureCookieUser string, ctx *gin.Context) (*wf.User, error) {
	if cookie, err := getCookieHashKey(ctx); err == nil {
		wu, err := wf.QueryUser_byHashLogin(cookie)
		if err != nil {
			log.Println("validateUserByCookie() - Error on loading: wf.QueryUser_byLogin", err)
			return nil, err
		}
		if wu != nil {
			if wu.Username == secureCookieUser { //Need to make sure the cookie hash and secureCookie name is same
				return wu, nil
			}
		}
		return nil, ERROR_ON_SECURECOOKIE_HASHKEY
	}
	return nil, ERROR_WF_USER_NULL
}
func destroyAuthCookie(ctx *gin.Context) error {
	ssid, err := decodeCookie(ctx)
	if err != nil {
		return err
	}
	if checkingMemcache() {
		acm := new(AuthCookieManager)
		if err = gobcache.GetFromMemcache("Session_"+ssid, &acm); err != nil {
			log.Println("[GetFromMemcache] key `Session` is empty!")
			return err
		}
		if acm.LoggedIn && acm.UserId != 0 {
			if err = updateTimeStampToMemcacheSSID(ssid, acm.UserId, false); err != nil {
				log.Printf("Error on destroyAuthCookie(): %v", err)
				return err
			}
			return nil
		}
	}
	return ERROR_MEMCACHE_FAILED
}
func Logout(ctx *gin.Context) error {
	return destroyAuthCookie(ctx)
}

func fetchCookieFromRequest(r *http.Request) (string, error) {
	cookie, err := r.Cookie("cention-suiteSSID")
	if err != nil {
		return "", err
	}
	return cookie.Value, nil
}
func GetWebframeworkUserFromRequest(r *http.Request) int {
	ssid, err := fetchCookieFromRequest(r)
	if err != nil {
		log.Printf("Error on getting SSID: %v", err)
		return 0
	}
	if ssid == "" {
		return 0
	}
	acm := new(AuthCookieManager)
	if checkingMemcache() {
		if err := gobcache.GetFromMemcache("Session_"+ssid, &acm); err != nil {
			log.Println("[GetFromMemcache] key `Session` is empty!")
			return 0
		}
		log.Printf("Acm: %v", acm)
		if !acm.LoggedIn {
			return 0
		}
		//Update timestamp for every request
		if err := updateTimeStampToMemcacheSSID(ssid, acm.UserId, true); err != nil {
			log.Printf("Error on %v", err)
			return 0
		}
		return acm.UserId
	}
	return 0
}

func updateTimeStampToMemcacheSSID(ssid string, uid int, loginStatus bool) error {
	acm1 := new(AuthCookieManager)
	acm := new(AuthCookieManager)
	lastTimeGetRequest := time.Now().Unix()
	acm.LastLoginTime = lastTimeGetRequest
	acm.UserId = uid
	acm.LoggedIn = loginStatus
	log.Println("Time after:", acm.LastLoginTime)
	if err := gobcache.SaveInMemcache("Session_"+ssid, acm); err != nil {
		log.Println("[`SaveInMemcache`] Error on saving:", err)
		return err
	}
	if err := gobcache.GetFromMemcache("Session_"+ssid, &acm1); err != nil {
		log.Println("[`SaveInMemcache`] Error on saving:", err)
		return err
	}
	log.Println("HHHHHHH:", acm1)
	return nil
}

func Middleware() func(*gin.Context) {
	return func(ctx *gin.Context) {
		if strings.HasPrefix(ctx.Request.RequestURI, "/debug/pprof/") {
			ctx.Next()
			return
		}
		wfUserId := GetWebframeworkUserFromRequest(ctx.Request)
		if wfUserId == 0 {
			ctx.AbortWithStatus(HTTP_UNAUTHORIZE_ACCESS)
			return
		}
		currUser := controllers.FetchUserObject(wfUserId)
		ctx.Keys = make(map[string]interface{})
		ctx.Keys["loggedInUser"] = currUser
		ctx.Next()
	}
}
<|endoftext|>


<commit_before>package memo

// Func is the type of the function to memoize.
type Func func(key string, done <-chan struct{}) (interface{}, error)

type result struct {
	value interface{}
	err   error
}

type entry struct {
	res   result
	ready chan struct{}
}

type request struct {
	key      string
	response chan<- result
	done     <-chan struct{} // cancellation channel
}

// Memo implements simple memoization of a function.
type Memo struct {
	requests, cancels chan request
}

// New initialized memoizarion structure. Clients must call Close when done.
func New(f Func) *Memo {
	memo := &Memo{
		requests: make(chan request),
		cancels:  make(chan request),
	}
	go memo.server(f)
	return memo
}

// Get calculates new result for Func or returns one
// from the cache.
func (m *Memo) Get(key string, done <-chan struct{}) (interface{}, error) {
	response := make(chan result)
	req := request{
		key:      key,
		response: response,
		done:     done,
	}
	m.requests <- req
	res := <-response
	select {
	case <-done:
		m.cancels <- req
	default:
	}
	return res.value, res.err
}

// Close closes internal channel for requests.
func (m *Memo) Close() {
	close(m.requests)
	close(m.cancels)
}

func (m *Memo) server(f Func) {
	cache := make(map[string]*entry)

	// Don't create new method for this to do not pass cache outside server.
	revokeRequests := func() {
		for {
			select {
			case req := <-m.cancels:
				delete(cache, req.key)
			default:
				return
			}
		}
	}

	for {
		// Perform all cancellation requests before start execution.
		revokeRequests()

		select {
		case req := <-m.cancels:
			delete(cache, req.key)
		case req := <-m.requests:
			e := cache[req.key]
			if e == nil {
				e = &entry{ready: make(chan struct{})}
				cache[req.key] = e
				go e.call(f, req.key, req.done)
			}
			go e.deliver(req.response)
		}
	}
}

func (e *entry) call(f Func, key string, done <-chan struct{}) {
	e.res.value, e.res.err = f(key, done)
	close(e.ready)
}

func (e *entry) deliver(response chan<- result) {
	<-e.ready
	response <- e.res
}
<commit_msg>[9.3] fix type<commit_after>package memo

// Func is the type of the function to memoize.
type Func func(key string, done <-chan struct{}) (interface{}, error)

type result struct {
	value interface{}
	err   error
}

type entry struct {
	res   result
	ready chan struct{}
}

type request struct {
	key      string
	response chan<- result
	done     <-chan struct{} // cancellation channel
}

// Memo implements simple memorization of a function.
type Memo struct {
	requests, cancels chan request
}

// New initialized memoizarion structure. Clients must call Close when done.
func New(f Func) *Memo {
	memo := &Memo{
		requests: make(chan request),
		cancels:  make(chan request),
	}
	go memo.server(f)
	return memo
}

// Get calculates new result for Func or returns one
// from the cache.
func (m *Memo) Get(key string, done <-chan struct{}) (interface{}, error) {
	response := make(chan result)
	req := request{
		key:      key,
		response: response,
		done:     done,
	}
	m.requests <- req
	res := <-response
	select {
	case <-done:
		m.cancels <- req
	default:
	}
	return res.value, res.err
}

// Close closes internal channel for requests.
func (m *Memo) Close() {
	close(m.requests)
	close(m.cancels)
}

func (m *Memo) server(f Func) {
	cache := make(map[string]*entry)

	// Don't create new method for this to do not pass cache outside server.
	revokeRequests := func() {
		for {
			select {
			case req := <-m.cancels:
				delete(cache, req.key)
			default:
				return
			}
		}
	}

	for {
		// Perform all cancellation requests before start execution.
		revokeRequests()

		select {
		case req := <-m.cancels:
			delete(cache, req.key)
		case req := <-m.requests:
			e := cache[req.key]
			if e == nil {
				e = &entry{ready: make(chan struct{})}
				cache[req.key] = e
				go e.call(f, req.key, req.done)
			}
			go e.deliver(req.response)
		}
	}
}

func (e *entry) call(f Func, key string, done <-chan struct{}) {
	e.res.value, e.res.err = f(key, done)
	close(e.ready)
}

func (e *entry) deliver(response chan<- result) {
	<-e.ready
	response <- e.res
}
<|endoftext|>