<commit_before>package slack

import (
	"bytes"
	"log"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/eveopsec/zk2s/zk2s/config"
	"github.com/eveopsec/zk2s/zk2s/filter"
	"github.com/eveopsec/zk2s/zk2s/tmpl"
	slacklib "github.com/nlopes/slack"
	"github.com/vivace-io/evelib/crest"
	"github.com/vivace-io/evelib/zkill"
)

// data is passed to templates for defining how a slacklib post appears.
type data struct {
	Killmail       crest.Killmail
	TotalValue     string
	IsLoss         bool
	IsSolo         bool
	InAlli         bool
	LosingCorp     string
	LosingAlli     string
	CorpsInvolved  []string
	AlliInvolved   []string
	PilotInvolved  []string
	FinalBlowPilot []string
	FinalBlowCorp  []string
	FinalBlowAlli  []string
	TotalCorp      []string
	TotalAlli      []string
}

// format loads the formatting template and applies formatting
// rules from the Configuration object.
func format(kill zkill.Kill, channel config.Channel) (messageParams slacklib.PostMessageParameters) {
	title := new(bytes.Buffer)
	body := new(bytes.Buffer)
	var err error

	// define post data for templates
	d := new(data)
	d.Killmail = *kill.Killmail
	d.TotalValue = humanize.Comma(int64(kill.Zkb.TotalValue))
	d.IsLoss = filter.IsLoss(kill, channel)
	//Solo kill testing
	if len(kill.Killmail.Attackers) == 1 {
		d.IsSolo = true
	} else {
		d.IsLoss = false
	}
	//Testing to see if the victim is in an alliance

	if kill.Killmail.Victim.Alliance.Name != "" {
		d.InAlli = true
		d.LosingAlli = kill.Killmail.Victim.Alliance.Name
	}
	d.LosingCorp = kill.Killmail.Victim.Corporation.Name

	// Compile list of pilots involved, if not final blow
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd = false
		}
		if kill.Killmail.Attackers[a].Character.Name == "" {
			okToAdd = false
		}
		if okToAdd {
			d.PilotInvolved = append(d.PilotInvolved, kill.Killmail.Attackers[a].Character.Name)
		}
	}

	//Compile the list for the final blow pilot, mainly use for formatting commas on the post
	for a := range kill.Killmail.Attackers {
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd := true
			if kill.Killmail.Attackers[a].Character.Name == "" {
				okToAdd = false
			}
			if okToAdd {
				d.FinalBlowPilot = append(d.FinalBlowPilot, kill.Killmail.Attackers[a].Character.Name)
				d.FinalBlowCorp = append(d.FinalBlowCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.FinalBlowAlli = append(d.FinalBlowAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
			}

		}
	}
	// Compile list of corporations involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		for c := range d.CorpsInvolved {
			if kill.Killmail.Attackers[a].Corporation.Name == d.CorpsInvolved[c] {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Corporation.Name == d.FinalBlowCorp[c] {
				okToAdd = false
				break
			}
			if okToAdd {
				d.CorpsInvolved = append(d.CorpsInvolved, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
			}
		}
	}
	// Compile list of alliances involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {

		okToAdd := true

		for c := range d.AlliInvolved {

			// Do not add blank alliances (corp is not in an alliance)
			if kill.Killmail.Attackers[a].Alliance.Name == "" {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.AlliInvolved[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.FinalBlowAlli[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if okToAdd {
				d.AlliInvolved = append(d.AlliInvolved, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.InAlli = true
			}
		}
	}

	// Execute templates
	err = tmpl.T.ExecuteTemplate(title, "kill-title", d)
	if err != nil {
		log.Println(err)
	}
	err = tmpl.T.ExecuteTemplate(body, "kill-body", d)
	if err != nil {
		log.Println(err)
	}

	attch := slacklib.Attachment{}
	attch.MarkdownIn = []string{"pretext", "text"}
	attch.Title = title.String()
	attch.TitleLink = "https://zkillboard.com/kill/" + strconv.Itoa(kill.KillID) + "/"
	attch.ThumbURL = "http://image.eveonline.com/render/" + strconv.Itoa(kill.Killmail.Victim.ShipType.ID) + "_64.png"
	attch.Text = body.String()
	//Color Coding
	if filter.IsLoss(kill, channel) {
		attch.Color = "danger"
	} else {
		attch.Color = "good"
	}
	messageParams.Attachments = []slacklib.Attachment{attch}
	messageParams.AsUser = true
	return
}
<commit_msg>fixed invalid indirect<commit_after>package slack

import (
	"bytes"
	"log"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/eveopsec/zk2s/zk2s/config"
	"github.com/eveopsec/zk2s/zk2s/filter"
	"github.com/eveopsec/zk2s/zk2s/tmpl"
	slacklib "github.com/nlopes/slack"
	"github.com/vivace-io/evelib/crest"
	"github.com/vivace-io/evelib/zkill"
)

// data is passed to templates for defining how a slacklib post appears.
type data struct {
	Killmail       crest.Killmail
	TotalValue     string
	IsLoss         bool
	IsSolo         bool
	InAlli         bool
	LosingCorp     string
	LosingAlli     string
	CorpsInvolved  []string
	AlliInvolved   []string
	PilotInvolved  []string
	FinalBlowPilot []string
	FinalBlowCorp  []string
	FinalBlowAlli  []string
	TotalCorp      []string
	TotalAlli      []string
}

// format loads the formatting template and applies formatting
// rules from the Configuration object.
func format(kill zkill.Kill, channel config.Channel) (messageParams slacklib.PostMessageParameters) {
	title := new(bytes.Buffer)
	body := new(bytes.Buffer)
	var err error

	// define post data for templates
	d := new(data)
	d.Killmail = kill.Killmail
	d.TotalValue = humanize.Comma(int64(kill.Zkb.TotalValue))
	d.IsLoss = filter.IsLoss(kill, channel)
	//Solo kill testing
	if len(kill.Killmail.Attackers) == 1 {
		d.IsSolo = true
	} else {
		d.IsLoss = false
	}
	//Testing to see if the victim is in an alliance

	if kill.Killmail.Victim.Alliance.Name != "" {
		d.InAlli = true
		d.LosingAlli = kill.Killmail.Victim.Alliance.Name
	}
	d.LosingCorp = kill.Killmail.Victim.Corporation.Name

	// Compile list of pilots involved, if not final blow
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd = false
		}
		if kill.Killmail.Attackers[a].Character.Name == "" {
			okToAdd = false
		}
		if okToAdd {
			d.PilotInvolved = append(d.PilotInvolved, kill.Killmail.Attackers[a].Character.Name)
		}
	}

	//Compile the list for the final blow pilot, mainly use for formatting commas on the post
	for a := range kill.Killmail.Attackers {
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd := true
			if kill.Killmail.Attackers[a].Character.Name == "" {
				okToAdd = false
			}
			if okToAdd {
				d.FinalBlowPilot = append(d.FinalBlowPilot, kill.Killmail.Attackers[a].Character.Name)
				d.FinalBlowCorp = append(d.FinalBlowCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.FinalBlowAlli = append(d.FinalBlowAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
			}

		}
	}
	// Compile list of corporations involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		for c := range d.CorpsInvolved {
			if kill.Killmail.Attackers[a].Corporation.Name == d.CorpsInvolved[c] {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Corporation.Name == d.FinalBlowCorp[c] {
				okToAdd = false
				break
			}
			if okToAdd {
				d.CorpsInvolved = append(d.CorpsInvolved, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
			}
		}
	}
	// Compile list of alliances involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {

		okToAdd := true

		for c := range d.AlliInvolved {

			// Do not add blank alliances (corp is not in an alliance)
			if kill.Killmail.Attackers[a].Alliance.Name == "" {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.AlliInvolved[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.FinalBlowAlli[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if okToAdd {
				d.AlliInvolved = append(d.AlliInvolved, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.InAlli = true
			}
		}
	}

	// Execute templates
	err = tmpl.T.ExecuteTemplate(title, "kill-title", d)
	if err != nil {
		log.Println(err)
	}
	err = tmpl.T.ExecuteTemplate(body, "kill-body", d)
	if err != nil {
		log.Println(err)
	}

	attch := slacklib.Attachment{}
	attch.MarkdownIn = []string{"pretext", "text"}
	attch.Title = title.String()
	attch.TitleLink = "https://zkillboard.com/kill/" + strconv.Itoa(kill.KillID) + "/"
	attch.ThumbURL = "http://image.eveonline.com/render/" + strconv.Itoa(kill.Killmail.Victim.ShipType.ID) + "_64.png"
	attch.Text = body.String()
	//Color Coding
	if filter.IsLoss(kill, channel) {
		attch.Color = "danger"
	} else {
		attch.Color = "good"
	}
	messageParams.Attachments = []slacklib.Attachment{attch}
	messageParams.AsUser = true
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	key       string
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
	status    string
	crash1    bool
	crash2    bool
	base      string
	block     bool
	comp      chan bool
}

//Scheduler the main task scheduler
type Scheduler struct {
	Log              func(string)
	blockingQueue    chan *rCommand
	nonblockingQueue chan *rCommand
	complete         []*rCommand
}

func (s *Scheduler) getState(key string) string {
	for _, c := range s.complete {
		if c.key == key {
			return fmt.Sprintf("%v -> %v", c.endTime, c.output)
		}
	}

	return "UNKNOWN"
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	debug.PrintStack()
	fmt.Printf("Scheduling: %v\n", c.command.Path)
	key := fmt.Sprintf("%v", time.Now().UnixNano())
	s.complete = append(s.complete, c)
	c.status = "InQueue"
	c.key = key
	if c.block {
		s.blockingQueue <- c
	} else {
		s.nonblockingQueue <- c
	}
	return key
}

func (s *Scheduler) getOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.output, nil
		}
	}

	return key, fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) getErrOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.mainOut, nil
		}
	}

	return "", fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) wait(key string) {
	for _, c := range s.complete {
		if c.key == key {
			fmt.Printf("Waiting for %v\n", key)
			<-c.comp
			fmt.Printf("Waited for %v\n", key)
			return
		}
	}
	fmt.Printf("Wait Failed for %v with %v\n", key, len(s.complete))
}

func (s *Scheduler) getStatus(key string) string {
	for _, val := range s.complete {
		if val.key == key {
			return val.status
		}
	}

	return fmt.Sprintf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) killJob(key string) {
	for _, val := range s.complete {
		if val.key == key {
			if val.command.Process != nil {
				val.command.Process.Kill()
				val.command.Process.Wait()
			}
		}
	}
}

func (s *Scheduler) processBlockingCommands() {
	for c := range s.blockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func (s *Scheduler) processNonblockingCommands() {
	for c := range s.nonblockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func run(c *rCommand) error {
	c.status = "Running"
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err1 := c.command.StderrPipe()
	outr, err2 := c.command.StdoutPipe()

	if c.crash1 || err1 != nil {
		return err1
	}

	if c.crash2 || err2 != nil {
		return err2
	}

	scanner := bufio.NewScanner(out)
	go func() {
		for scanner != nil && scanner.Scan() {
			c.output += scanner.Text()
		}
		out.Close()
	}()

	scanner2 := bufio.NewScanner(outr)
	go func() {
		for scanner2 != nil && scanner2.Scan() {
			c.mainOut += scanner2.Text()
		}
		outr.Close()
	}()

	c.status = "StartCommand"
	err := c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	r := func() {
		c.status = "Entering Wait"
		err := c.command.Wait()
		c.status = "Completed Wait"
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
		c.comp <- true
	}

	if c.block {
		r()
	} else {
		go r()
	}

	return nil
}
<commit_msg>More output<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	key       string
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
	status    string
	crash1    bool
	crash2    bool
	base      string
	block     bool
	comp      chan bool
}

//Scheduler the main task scheduler
type Scheduler struct {
	Log              func(string)
	blockingQueue    chan *rCommand
	nonblockingQueue chan *rCommand
	complete         []*rCommand
}

func (s *Scheduler) getState(key string) string {
	for _, c := range s.complete {
		if c.key == key {
			return fmt.Sprintf("%v -> %v", c.endTime, c.output)
		}
	}

	return "UNKNOWN"
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	debug.PrintStack()
	fmt.Printf("Scheduling: %v\n", c.command.Path)
	key := fmt.Sprintf("%v", time.Now().UnixNano())
	s.complete = append(s.complete, c)
	c.status = "InQueue"
	c.key = key
	if c.block {
		s.blockingQueue <- c
	} else {
		s.nonblockingQueue <- c
	}
	return key
}

func (s *Scheduler) getOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.output, nil
		}
	}

	return key, fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) getErrOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.mainOut, nil
		}
	}

	return "", fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) wait(key string) {
	for _, c := range s.complete {
		if c.key == key {
			fmt.Printf("Waiting for %v\n", key)
			<-c.comp
			fmt.Printf("Waited for %v\n", key)
			return
		}
	}
	fmt.Printf("Wait Failed for %v with %v\n", key, len(s.complete))
}

func (s *Scheduler) getStatus(key string) string {
	for _, val := range s.complete {
		if val.key == key {
			return val.status
		}
	}

	return fmt.Sprintf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) killJob(key string) {
	for _, val := range s.complete {
		if val.key == key {
			if val.command.Process != nil {
				val.command.Process.Kill()
				val.command.Process.Wait()
			}
		}
	}
}

func (s *Scheduler) processBlockingCommands() {
	for c := range s.blockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func (s *Scheduler) processNonblockingCommands() {
	for c := range s.nonblockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func run(c *rCommand) error {
	c.status = "Running"
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err1 := c.command.StderrPipe()
	outr, err2 := c.command.StdoutPipe()

	if c.crash1 || err1 != nil {
		return err1
	}

	if c.crash2 || err2 != nil {
		return err2
	}

	scanner := bufio.NewScanner(out)
	go func() {
		for scanner != nil && scanner.Scan() {
			c.output += scanner.Text()
		}
		out.Close()
	}()

	scanner2 := bufio.NewScanner(outr)
	go func() {
		for scanner2 != nil && scanner2.Scan() {
			c.mainOut += scanner2.Text()
		}
		outr.Close()
	}()

	c.status = "StartCommand"
	err := c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	r := func() {
		c.status = "Entering Wait"
		err := c.command.Wait()
		c.status = "Completed Wait"
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
		fmt.Printf("ENDED - Writing to channel\n")
		c.comp <- true
		fmt.Printf("COMPLETE\n")
	}

	if c.block {
		r()
	} else {
		go r()
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright ©2016 The go-lsst Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/go-lsst/fcs-lpc-motor-ctl/bench"
	"github.com/go-lsst/ncs/drivers/m702"
)

type scriptCmd func(args []string, w io.Writer) (m702.Parameter, error)

type Script struct {
	srv   *server
	cmds  map[string]scriptCmd
	motor bench.Motor
}

func (sc *Script) displayParam(w io.Writer, p m702.Parameter) {
	fmt.Fprintf(
		w,
		"Pr-%v: %s (%v)\n",
		p,
		sc.displayBytes(p.Data[:]),
		codec.Uint32(p.Data[:]),
	)
}

func (sc *Script) displayBytes(o []byte) string {
	hex := make([]string, len(o))
	dec := make([]string, len(o))
	for i, v := range o {
		hex[i] = fmt.Sprintf("0x%02x", v)
		dec[i] = fmt.Sprintf("%3d", v)
	}
	return fmt.Sprintf("hex=%s dec=%s", hex, dec)
}

func (sc *Script) cmdGet(args []string, w io.Writer) (m702.Parameter, error) {
	param, err := sc.parseParam(args[0])
	if err != nil {
		return param, err
	}
	err = sc.motor.ReadParam(&param)
	if err == nil {
		fmt.Fprintf(w, "<<< ")
		sc.displayParam(w, param)
	}

	return param, err
}

func (sc *Script) cmdSet(args []string, w io.Writer) (m702.Parameter, error) {
	param, err := sc.parseParam(args[0])
	if err != nil {
		return param, err
	}
	vtype := "u32"
	if len(args) > 1 && len(args[1]) > 0 && string(args[1][0]) == "-" {
		vtype = "i32"
	}
	if len(args) > 2 {
		vtype = args[2]
	}

	switch vtype {
	case "u32", "uint32":
		vv, err := strconv.ParseUint(args[1], 10, 32)
		if err != nil {
			return param, err
		}
		codec.PutUint32(param.Data[:], uint32(vv))

	case "i32", "int32":
		vv, err := strconv.ParseInt(args[1], 10, 32)
		if err != nil {
			return param, err
		}
		codec.PutUint32(param.Data[:], uint32(vv))

	default:
		return param, bench.FcsError{200, fmt.Sprintf("invalid value-type (%v)", vtype)}
	}

	err = sc.motor.WriteParam(param)
	if err == nil {
		fmt.Fprintf(w, "<<< ")
		sc.displayParam(w, param)
	}

	return param, err
}

func (sc *Script) cmdMotor(args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	switch len(args) {
	case 0:
		// get
		return p, bench.FcsError{200, fmt.Sprintf("invalid number of arguments (got=%d, want=1|2)", len(args))}
	case 1:
		// set
		motors := sc.srv.motors()
		switch strings.ToLower(args[0]) {
		case "x":
			sc.motor = motors[0].Motor()
		case "z":
			sc.motor = motors[1].Motor()
		default:
			return p, bench.FcsError{200, fmt.Sprintf("invalid motor name (got=%v, want=x|z)", args[0])}
		}
		return p, nil
	default:
		return p, bench.FcsError{200, fmt.Sprintf("invalid number of arguments (got=%d, want=1|2)", len(args))}
	}
}

func (sc *Script) cmdFindHomeX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdFindHome(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdPosX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdPos(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdRPMX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdRPM(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdAnglePosX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdAnglePos(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdFindHomeZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdFindHome(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdPosZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdPos(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdRPMZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdRPM(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdAnglePosZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdAnglePos(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdFindHome(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	params := append([]m702.Parameter{},
		newParameter(bench.ParamCmdReady),
		newParameter(bench.ParamModePos),
		newParameter(bench.ParamHome),
		newParameter(bench.ParamCmdReady),
	)

	codec.PutUint32(params[0].Data[:], 0)
	codec.PutUint32(params[1].Data[:], 0)
	codec.PutUint32(params[2].Data[:], 1)
	codec.PutUint32(params[3].Data[:], 1)

	for _, p := range params {
		err := m.Motor().WriteParam(p)
		if err != nil {
			return p, fmt.Errorf("error writing parameter %v to motor-%v: %v", p, m.name, err)
		}
	}

	return p, nil
}

func (sc *Script) cmdPos(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	params := append([]m702.Parameter{},
		newParameter(bench.ParamCmdReady),
		newParameter(bench.ParamModePos),
		newParameter(bench.ParamHome),
		newParameter(bench.ParamCmdReady),
	)

	codec.PutUint32(params[0].Data[:], 0)
	codec.PutUint32(params[1].Data[:], 1)
	codec.PutUint32(params[2].Data[:], 0)
	codec.PutUint32(params[3].Data[:], 1)

	for _, p := range params {
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, fmt.Errorf("error writing parameter %v to motor-%v: %v", p, m.name, err)
		}
	}

	return p, nil
}

func (sc *Script) cmdRPM(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	switch len(args) {
	case 0:
		// TODO(sbinet): only retrieve the needed infos.
		info, err := m.infos(1 * time.Second)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "get-%v-rpm=%d\n", m.name, info.RPMs)
	case 1:
		rpm, err := strconv.Atoi(args[0])
		if err != nil {
			return p, err
		}
		switch {
		case rpm > 3000:
			return p, fmt.Errorf("invalid RPM value (%v > 3000)", rpm)
		case rpm < 0:
			return p, fmt.Errorf("invalid RPM value (%v < 0)", rpm)
		}
		p = newParameter(bench.ParamRPMs)
		codec.PutUint32(p.Data[:], uint32(rpm))
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "set-%v-rpm=%d\n", m.name, rpm)
		return p, nil

	default:
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=0,1)", len(args))
	}
	return p, nil
}

func (sc *Script) cmdAnglePos(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	switch len(args) {
	case 0:
		// TODO(sbinet): only retrieve the needed infos.
		info, err := m.infos(1 * time.Second)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "get-%v-angle-pos=%d\n", m.name, info.Angle)
	case 1:
		angle, err := strconv.Atoi(args[0])
		if err != nil {
			return p, err
		}
		switch {
		case angle > +90:
			return p, fmt.Errorf("invalid angle position (%v > +90.0)", angle)

		case angle < -90:
			return p, fmt.Errorf("invalid angle position (%v < -90.0)", angle)
		}

		p = newParameter(bench.ParamWritePos)
		codec.PutUint32(p.Data[:], uint32(angle*10))
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "set-%v-angle-pos=%d\n", m.name, angle)
		return p, nil

	default:
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=0,1)", len(args))
	}
	return p, nil
}

func (sc *Script) cmdSleep(args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	if len(args) != 1 {
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=1)", len(args))
	}

	t, err := time.ParseDuration(args[0])
	if err != nil {
		return p, fmt.Errorf("fcs: could not parse 'sleep %s': %v", args[0], err)
	}

	time.Sleep(t)
	return p, nil
}

func (sc *Script) run(motor bench.Motor, r io.Reader, w io.Writer) error {
	var (
		err      error
		oldMotor = sc.motor
	)
	defer func() {
		sc.motor = oldMotor
	}()

	sc.motor = motor
	scan := bufio.NewScanner(r)
	for scan.Scan() {
		txt := strings.TrimSpace(scan.Text())
		if txt == "" {
			continue
		}
		toks := strings.Split(txt, " ")
		_, err = sc.dispatch(toks, w)
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return err
		}
	}

	err = scan.Err()
	if err == io.EOF {
		err = nil
	}
	return err
}

func (sc *Script) dispatch(toks []string, w io.Writer) (m702.Parameter, error) {
	fct, ok := sc.cmds[toks[0]]
	if !ok {
		var p m702.Parameter
		return p, bench.FcsError{200, fmt.Sprintf("invalid command verb [%s]", toks[0])}
	}
	// fmt.Fprintf(w, ">>> %s\n", strings.Join(toks, " "))
	return fct(toks[1:], w)
}

func (sc *Script) check(m *motor) error {
	online, err := m.isOnline(1 * time.Second)
	if err != nil {
		return fmt.Errorf("fcs: error checking whether motor-%v is online: %v", m.name, err)
	}
	if !online {
		return bench.ErrMotorOffline
		// return fmt.Sprintf("fcs: motor-%v is NOT ONLINE", m.name)
	}
	if m.isHWLocked() {
		return bench.ErrMotorHWLock
	}
	if m.isManual() {
		return bench.ErrMotorManual
	}
	return nil
}

func (sc *Script) parseParam(arg string) (m702.Parameter, error) {
	if !strings.Contains(arg, ".") {
		var p m702.Parameter
		return p, bench.FcsError{200, fmt.Sprintf("invalid parameter (%s)", arg)}
	}
	return m702.NewParameter(arg)
}

func newScripter(srv *server, motor bench.Motor) Script {
	var script Script
	script = Script{
		srv: srv,
		cmds: map[string]scriptCmd{
			"get":         script.cmdGet,
			"set":         script.cmdSet,
			"sleep":       script.cmdSleep,
			"motor":       script.cmdMotor,
			"x-find-home": script.cmdFindHomeX,
			"x-pos":       script.cmdPosX,
			"x-rpm":       script.cmdRPMX,
			"x-angle-pos": script.cmdAnglePosX,
			"z-find-home": script.cmdFindHomeZ,
			"z-pos":       script.cmdPosZ,
			"z-rpm":       script.cmdRPMZ,
			"z-angle-pos": script.cmdAnglePosZ,
		},
		motor: motor,
	}
	return script
}
<commit_msg>scripting: make get/set less verbose<commit_after>// Copyright ©2016 The go-lsst Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/go-lsst/fcs-lpc-motor-ctl/bench"
	"github.com/go-lsst/ncs/drivers/m702"
)

type scriptCmd func(args []string, w io.Writer) (m702.Parameter, error)

type Script struct {
	srv   *server
	cmds  map[string]scriptCmd
	motor bench.Motor
}

func (sc *Script) displayParam(w io.Writer, p m702.Parameter) {
	fmt.Fprintf(
		w,
		"Pr-%v: %s (%v)\n",
		p,
		sc.displayBytes(p.Data[:]),
		codec.Uint32(p.Data[:]),
	)
}

func (sc *Script) displayBytes(o []byte) string {
	hex := make([]string, len(o))
	dec := make([]string, len(o))
	for i, v := range o {
		hex[i] = fmt.Sprintf("0x%02x", v)
		dec[i] = fmt.Sprintf("%3d", v)
	}
	return fmt.Sprintf("hex=%s dec=%s", hex, dec)
}

func (sc *Script) cmdGet(args []string, w io.Writer) (m702.Parameter, error) {
	param, err := sc.parseParam(args[0])
	if err != nil {
		return param, err
	}
	err = sc.motor.ReadParam(&param)
	if err == nil {
		// fmt.Fprintf(w, "<<< ")
		sc.displayParam(w, param)
	}

	return param, err
}

func (sc *Script) cmdSet(args []string, w io.Writer) (m702.Parameter, error) {
	param, err := sc.parseParam(args[0])
	if err != nil {
		return param, err
	}
	vtype := "u32"
	if len(args) > 1 && len(args[1]) > 0 && string(args[1][0]) == "-" {
		vtype = "i32"
	}
	if len(args) > 2 {
		vtype = args[2]
	}

	switch vtype {
	case "u32", "uint32":
		vv, err := strconv.ParseUint(args[1], 10, 32)
		if err != nil {
			return param, err
		}
		codec.PutUint32(param.Data[:], uint32(vv))

	case "i32", "int32":
		vv, err := strconv.ParseInt(args[1], 10, 32)
		if err != nil {
			return param, err
		}
		codec.PutUint32(param.Data[:], uint32(vv))

	default:
		return param, bench.FcsError{200, fmt.Sprintf("invalid value-type (%v)", vtype)}
	}

	err = sc.motor.WriteParam(param)
	if err == nil {
		// fmt.Fprintf(w, "<<< ")
		sc.displayParam(w, param)
	}

	return param, err
}

func (sc *Script) cmdMotor(args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	switch len(args) {
	case 0:
		// get
		return p, bench.FcsError{200, fmt.Sprintf("invalid number of arguments (got=%d, want=1|2)", len(args))}
	case 1:
		// set
		motors := sc.srv.motors()
		switch strings.ToLower(args[0]) {
		case "x":
			sc.motor = motors[0].Motor()
		case "z":
			sc.motor = motors[1].Motor()
		default:
			return p, bench.FcsError{200, fmt.Sprintf("invalid motor name (got=%v, want=x|z)", args[0])}
		}
		return p, nil
	default:
		return p, bench.FcsError{200, fmt.Sprintf("invalid number of arguments (got=%d, want=1|2)", len(args))}
	}
}

func (sc *Script) cmdFindHomeX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdFindHome(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdPosX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdPos(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdRPMX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdRPM(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdAnglePosX(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdAnglePos(sc.srv.motors()[0], args, w)
}

func (sc *Script) cmdFindHomeZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdFindHome(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdPosZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdPos(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdRPMZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdRPM(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdAnglePosZ(args []string, w io.Writer) (m702.Parameter, error) {
	return sc.cmdAnglePos(sc.srv.motors()[1], args, w)
}

func (sc *Script) cmdFindHome(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	params := append([]m702.Parameter{},
		newParameter(bench.ParamCmdReady),
		newParameter(bench.ParamModePos),
		newParameter(bench.ParamHome),
		newParameter(bench.ParamCmdReady),
	)

	codec.PutUint32(params[0].Data[:], 0)
	codec.PutUint32(params[1].Data[:], 0)
	codec.PutUint32(params[2].Data[:], 1)
	codec.PutUint32(params[3].Data[:], 1)

	for _, p := range params {
		err := m.Motor().WriteParam(p)
		if err != nil {
			return p, fmt.Errorf("error writing parameter %v to motor-%v: %v", p, m.name, err)
		}
	}

	return p, nil
}

func (sc *Script) cmdPos(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	params := append([]m702.Parameter{},
		newParameter(bench.ParamCmdReady),
		newParameter(bench.ParamModePos),
		newParameter(bench.ParamHome),
		newParameter(bench.ParamCmdReady),
	)

	codec.PutUint32(params[0].Data[:], 0)
	codec.PutUint32(params[1].Data[:], 1)
	codec.PutUint32(params[2].Data[:], 0)
	codec.PutUint32(params[3].Data[:], 1)

	for _, p := range params {
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, fmt.Errorf("error writing parameter %v to motor-%v: %v", p, m.name, err)
		}
	}

	return p, nil
}

func (sc *Script) cmdRPM(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	switch len(args) {
	case 0:
		// TODO(sbinet): only retrieve the needed infos.
		info, err := m.infos(1 * time.Second)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "get-%v-rpm=%d\n", m.name, info.RPMs)
	case 1:
		rpm, err := strconv.Atoi(args[0])
		if err != nil {
			return p, err
		}
		switch {
		case rpm > 3000:
			return p, fmt.Errorf("invalid RPM value (%v > 3000)", rpm)
		case rpm < 0:
			return p, fmt.Errorf("invalid RPM value (%v < 0)", rpm)
		}
		p = newParameter(bench.ParamRPMs)
		codec.PutUint32(p.Data[:], uint32(rpm))
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "set-%v-rpm=%d\n", m.name, rpm)
		return p, nil

	default:
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=0,1)", len(args))
	}
	return p, nil
}

func (sc *Script) cmdAnglePos(m *motor, args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	err := sc.check(m)
	if err != nil {
		return p, err
	}

	switch len(args) {
	case 0:
		// TODO(sbinet): only retrieve the needed infos.
		info, err := m.infos(1 * time.Second)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "get-%v-angle-pos=%d\n", m.name, info.Angle)
	case 1:
		angle, err := strconv.Atoi(args[0])
		if err != nil {
			return p, err
		}
		switch {
		case angle > +90:
			return p, fmt.Errorf("invalid angle position (%v > +90.0)", angle)

		case angle < -90:
			return p, fmt.Errorf("invalid angle position (%v < -90.0)", angle)
		}

		p = newParameter(bench.ParamWritePos)
		codec.PutUint32(p.Data[:], uint32(angle*10))
		err = m.Motor().WriteParam(p)
		if err != nil {
			return p, err
		}
		fmt.Fprintf(w, "set-%v-angle-pos=%d\n", m.name, angle)
		return p, nil

	default:
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=0,1)", len(args))
	}
	return p, nil
}

func (sc *Script) cmdSleep(args []string, w io.Writer) (m702.Parameter, error) {
	var p m702.Parameter
	if len(args) != 1 {
		return p, fmt.Errorf("fcs: invalid number of parameters (got=%d. want=1)", len(args))
	}

	t, err := time.ParseDuration(args[0])
	if err != nil {
		return p, fmt.Errorf("fcs: could not parse 'sleep %s': %v", args[0], err)
	}

	time.Sleep(t)
	return p, nil
}

func (sc *Script) run(motor bench.Motor, r io.Reader, w io.Writer) error {
	var (
		err      error
		oldMotor = sc.motor
	)
	defer func() {
		sc.motor = oldMotor
	}()

	sc.motor = motor
	scan := bufio.NewScanner(r)
	for scan.Scan() {
		txt := strings.TrimSpace(scan.Text())
		if txt == "" {
			continue
		}
		toks := strings.Split(txt, " ")
		_, err = sc.dispatch(toks, w)
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return err
		}
	}

	err = scan.Err()
	if err == io.EOF {
		err = nil
	}
	return err
}

func (sc *Script) dispatch(toks []string, w io.Writer) (m702.Parameter, error) {
	fct, ok := sc.cmds[toks[0]]
	if !ok {
		var p m702.Parameter
		return p, bench.FcsError{200, fmt.Sprintf("invalid command verb [%s]", toks[0])}
	}
	// fmt.Fprintf(w, ">>> %s\n", strings.Join(toks, " "))
	return fct(toks[1:], w)
}

func (sc *Script) check(m *motor) error {
	online, err := m.isOnline(1 * time.Second)
	if err != nil {
		return fmt.Errorf("fcs: error checking whether motor-%v is online: %v", m.name, err)
	}
	if !online {
		return bench.ErrMotorOffline
		// return fmt.Sprintf("fcs: motor-%v is NOT ONLINE", m.name)
	}
	if m.isHWLocked() {
		return bench.ErrMotorHWLock
	}
	if m.isManual() {
		return bench.ErrMotorManual
	}
	return nil
}

func (sc *Script) parseParam(arg string) (m702.Parameter, error) {
	if !strings.Contains(arg, ".") {
		var p m702.Parameter
		return p, bench.FcsError{200, fmt.Sprintf("invalid parameter (%s)", arg)}
	}
	return m702.NewParameter(arg)
}

func newScripter(srv *server, motor bench.Motor) Script {
	var script Script
	script = Script{
		srv: srv,
		cmds: map[string]scriptCmd{
			"get":         script.cmdGet,
			"set":         script.cmdSet,
			"sleep":       script.cmdSleep,
			"motor":       script.cmdMotor,
			"x-find-home": script.cmdFindHomeX,
			"x-pos":       script.cmdPosX,
			"x-rpm":       script.cmdRPMX,
			"x-angle-pos": script.cmdAnglePosX,
			"z-find-home": script.cmdFindHomeZ,
			"z-pos":       script.cmdPosZ,
			"z-rpm":       script.cmdRPMZ,
			"z-angle-pos": script.cmdAnglePosZ,
		},
		motor: motor,
	}
	return script
}
<|endoftext|>


<commit_before>package semaphore

import (
	"sync"
	"sync/atomic"
)

// An integer-valued semaphore
type Semaphore struct {
	value     int64
	acquireMu sync.Mutex
	wake      chan struct{}
}

// Creates a new semaphore with initial value n. Panics if n is negative.
func New(n int) *Semaphore {
	if n < 0 {
		panic("negative initial value for a semaphore")
	}
	return &Semaphore{
		value: int64(n),
		wake:  make(chan struct{}, 1),
	}
}

// Tries to decrease the semaphore's value by n. If it is smaller than n, waits until it grows large enough.
func (s *Semaphore) Acquire(n int) {
	if n < 0 {
		panic("Semaphore.Acquire called with negative decrement")
	}
	v := atomic.LoadInt64(&s.value)
	for v >= int64(n) {
		if atomic.CompareAndSwapInt64(&s.value, v, v-int64(n)) {
			return
		}
		v = atomic.LoadInt64(&s.value)
	}
	s.acquireMu.Lock()
	v = atomic.AddInt64(&s.value, int64(-n))
	for v < 0 {
		<-s.wake
		v = atomic.LoadInt64(&s.value)
	}
	s.acquireMu.Unlock()
}

// Increases the semaphore's value by n. Will never sleep.
func (s *Semaphore) Release(n int) {
	if n < 0 {
		panic("Semaphore.Release called with negative increment")
	}
	v := atomic.AddInt64(&s.value, int64(n))
	if v-int64(n) < 0 && v >= 0 {
		select {
		case s.wake <- struct{}{}:
		default:
		}
	}
}

// Decreases the semaphore value to 0 and returns the difference. Can sleep.
func (s *Semaphore) Drain() int {
	s.acquireMu.Lock()
	v := atomic.LoadInt64(&s.value)
	atomic.AddInt64(&s.value, -v)
	s.acquireMu.Unlock()
	return int(v)
}
<commit_msg>Wakeup logic simplified.<commit_after>package semaphore

import (
	"sync"
	"sync/atomic"
)

// An integer-valued semaphore
type Semaphore struct {
	value     int64
	acquireMu sync.Mutex
	wake      chan struct{}
}

// Creates a new semaphore with initial value n. Panics if n is negative.
func New(n int) *Semaphore {
	if n < 0 {
		panic("negative initial value for a semaphore")
	}
	return &Semaphore{
		value: int64(n),
		wake:  make(chan struct{}, 1),
	}
}

// Tries to decrease the semaphore's value by n. If it is smaller than n, waits until it grows large enough.
func (s *Semaphore) Acquire(n int) {
	if n < 0 {
		panic("Semaphore.Acquire called with negative decrement")
	}
	v := atomic.LoadInt64(&s.value)
	for v >= int64(n) {
		if atomic.CompareAndSwapInt64(&s.value, v, v-int64(n)) {
			return
		}
		v = atomic.LoadInt64(&s.value)
	}
	s.acquireMu.Lock()
	v = atomic.AddInt64(&s.value, int64(-n))
	if v < 0 {
		<-s.wake
		v = atomic.LoadInt64(&s.value)
		if v < 0 {
			panic("semaphore: spurious wakeup")
		}
	}
	s.acquireMu.Unlock()
}

// Increases the semaphore's value by n. Will never sleep.
func (s *Semaphore) Release(n int) {
	if n < 0 {
		panic("Semaphore.Release called with negative increment")
	}
	v := atomic.AddInt64(&s.value, int64(n))
	if v-int64(n) < 0 && v >= 0 {
		select {
		case s.wake <- struct{}{}:
		default:
			panic("semaphore: unconsumed wakeup")
		}
	}
}

// Decreases the semaphore value to 0 and returns the difference. Can sleep.
func (s *Semaphore) Drain() int {
	s.acquireMu.Lock()
	v := atomic.LoadInt64(&s.value)
	atomic.AddInt64(&s.value, -v)
	s.acquireMu.Unlock()
	return int(v)
}
<|endoftext|>


<commit_before>package show

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/kdeploy/template"
	"github.com/flexiant/kdeploy/utils"
	"github.com/flexiant/kdeploy/webservice"
	gyml "github.com/ghodss/yaml"
)

// CmdShow implements the 'show' command
func CmdShow(c *cli.Context) {
	localKubePath, err := webservice.FetchKubeFromURL(os.Getenv("KDEPLOY_KUBEWARE"))
	utils.CheckError(err)

	log.Debugf("Going to parse kubeware in %s", localKubePath)

	metadata := template.ParseMetadata(localKubePath)
	defaults, err := metadata.AttributeDefaults()
	utils.CheckError(err)

	log.Debugf("Building attributes")
	attributes := template.BuildAttributes(os.Getenv("KDEPLOY_ATTRIBUTE"), defaults)

	log.Debugf("Parsing services")
	servicesSpecs, err := metadata.ParseServices(attributes)
	utils.CheckError(err)

	log.Debugf("Parsing controllers")
	controllersSpecs, err := metadata.ParseControllers(attributes)
	utils.CheckError(err)

	// print resolved resources
	for _, s := range servicesSpecs {
		y, err := gyml.JSONToYAML([]byte(s))
		utils.CheckError(err)
		fmt.Println(string(y))
	}
	for _, c := range controllersSpecs {
		y, err := gyml.JSONToYAML([]byte(c))
		utils.CheckError(err)
		fmt.Println(string(y))
	}
}
<commit_msg>fix mandatory param kubeware<commit_after>package show

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/flexiant/kdeploy/template"
	"github.com/flexiant/kdeploy/utils"
	"github.com/flexiant/kdeploy/webservice"
	gyml "github.com/ghodss/yaml"
)

// CmdShow implements the 'show' command
func CmdShow(c *cli.Context) {
	utils.CheckRequiredFlags(c, []string{"kubeware"})
	localKubePath, err := webservice.FetchKubeFromURL(os.Getenv("KDEPLOY_KUBEWARE"))
	utils.CheckError(err)

	log.Debugf("Going to parse kubeware in %s", localKubePath)

	metadata := template.ParseMetadata(localKubePath)
	defaults, err := metadata.AttributeDefaults()
	utils.CheckError(err)

	log.Debugf("Building attributes")
	attributes := template.BuildAttributes(os.Getenv("KDEPLOY_ATTRIBUTE"), defaults)

	log.Debugf("Parsing services")
	servicesSpecs, err := metadata.ParseServices(attributes)
	utils.CheckError(err)

	log.Debugf("Parsing controllers")
	controllersSpecs, err := metadata.ParseControllers(attributes)
	utils.CheckError(err)

	// print resolved resources
	for _, s := range servicesSpecs {
		y, err := gyml.JSONToYAML([]byte(s))
		utils.CheckError(err)
		fmt.Println(string(y))
	}
	for _, c := range controllersSpecs {
		y, err := gyml.JSONToYAML([]byte(c))
		utils.CheckError(err)
		fmt.Println(string(y))
	}
}
<|endoftext|>


<commit_before>package sign

import (
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"

	gossh "github.com/coreos/fleet/third_party/code.google.com/p/gosshnew/ssh"
	gosshagent "github.com/coreos/fleet/third_party/code.google.com/p/gosshnew/ssh/agent"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/pkg"
	"github.com/coreos/fleet/ssh"
)

const DefaultAuthorizedKeysFile = "~/.ssh/authorized_keys"

// SignatureSet contains a set of SSH signatures for a blob of data, and is named by a Tag.
type SignatureSet struct {
	Tag        string
	Signatures []*gossh.Signature
}

// SignatureCreator provides the ability to sign a blob of data with multiple SSH public keys, contained in the keyring
type SignatureCreator struct {
	keyring gosshagent.Agent
}

// NewSignatureCreator instantiates a SignatureCreator with the given keyring
func NewSignatureCreator(keyring gosshagent.Agent) *SignatureCreator {
	return &SignatureCreator{keyring}
}

// NewSignatureCreatorFromSSHAgent return a SignatureCreator which uses the local ssh-agent as its keyring
func NewSignatureCreatorFromSSHAgent() (*SignatureCreator, error) {
	keyring, err := ssh.SSHAgentClient()
	if err != nil {
		return nil, err
	}
	return &SignatureCreator{keyring}, nil
}

// Sign generates a SignatureSet for the given data, labelled by the supplied tag. It returns a *SignatureSet and any error encountere
func (sc *SignatureCreator) Sign(tag string, data []byte) (*SignatureSet, error) {
	if sc.keyring == nil {
		return nil, errors.New("signature creator is uninitialized")
	}

	sigs := make([]*gossh.Signature, 0)

	keys, err := sc.keyring.List()
	if err != nil {
		return nil, err
	}
	if len(keys) == 0 {
		return nil, errors.New("signature creator keyring is empty")
	}

	for _, k := range keys {
		sig, err := sc.keyring.Sign(k, data)
		if err != nil {
			return nil, err
		}

		sigs = append(sigs, sig)
	}

	return &SignatureSet{tag, sigs}, nil
}

type SignatureVerifier struct {
	// keys is used to verify signing, created when needed
	pubkeys []gossh.PublicKey
}

func NewSignatureVerifier() *SignatureVerifier {
	return &SignatureVerifier{}
}

// NewSignatureVerifierFromSSHAgent return SignatureVerifier which uses public keys in the local ssh-agent to verify signatures
func NewSignatureVerifierFromSSHAgent() (*SignatureVerifier, error) {
	keyring, err := ssh.SSHAgentClient()
	if err != nil {
		return nil, err
	}
	return NewSignatureVerifierFromKeyring(keyring)
}

// NewSignatureVerifierFromKeyring creates a SignatureVerifier
// which uses public keys from the given keyring to verify signatures
func NewSignatureVerifierFromKeyring(keyring gosshagent.Agent) (*SignatureVerifier, error) {
	keys, err := keyring.List()
	if err != nil {
		return nil, err
	}

	pubkeys := make([]gossh.PublicKey, len(keys))

	for i, k := range keys {
		pubkeys[i] = k
	}

	return &SignatureVerifier{pubkeys}, nil
}

// NewSignatureVerifierFromAuthorizedKeysFile creates a
// SignatureVerifier which uses public keys from the specified
// authorized_keys file to verify signatures
func NewSignatureVerifierFromAuthorizedKeysFile(filepath string) (*SignatureVerifier, error) {
	out, err := ioutil.ReadFile(pkg.ParseFilepath(filepath))
	if err != nil {
		return nil, err
	}

	pubkeys, err := parseAuthorizedKeys(out)
	if err != nil {
		return nil, err
	}
	if len(pubkeys) == 0 {
		return nil, errors.New("no authorized keys found in file")
	}

	return &SignatureVerifier{pubkeys}, nil
}

// Verify verifies that at least one of the signatures in the provided
// SignatureSet is a valid signature of the given data blob.
func (sv *SignatureVerifier) Verify(data []byte, s *SignatureSet) (bool, error) {
	if sv.pubkeys == nil {
		return false, errors.New("signature verifier is uninitialized")
	}

	for _, authKey := range sv.pubkeys {
		// Manually coerce the keys provided by the agent to PublicKeys,
		// since gosshnew does not want to do it for us and agent.Keys are unable to Verify
		key, err := gossh.ParsePublicKey(authKey.Marshal())
		if err != nil {
			log.Errorf("Unable to use SSH key: %v", err)
			continue
		}

		for _, sign := range s.Signatures {
			if err := key.Verify(data, sign); err == nil {
				return true, nil
			}
		}
	}

	return false, nil
}

func parseAuthorizedKeys(in []byte) ([]gossh.PublicKey, error) {
	pubkeys := make([]gossh.PublicKey, 0)
	for len(in) > 0 {
		pubkey, _, _, rest, err := gossh.ParseAuthorizedKey(in)
		if err != nil {
			return nil, err
		}
		in = rest

		pubkeys = append(pubkeys, pubkey)
	}

	return pubkeys, nil
}

func marshal(obj interface{}) ([]byte, error) {
	encoded, err := json.Marshal(obj)
	if err == nil {
		return encoded, nil
	} else {
		return nil, errors.New(fmt.Sprintf("Unable to JSON-serialize object: %s", err))
	}
}

func parseString(in []byte) (out, rest []byte, ok bool) {
	if len(in) < 4 {
		return
	}
	// First 4-byte is the length of the field
	length := binary.BigEndian.Uint32(in)
	if uint32(len(in)) < length+4 {
		return
	}
	return in[4 : length+4], in[length+4:], true
}
<commit_msg>chore(sign): remove unused parseString function<commit_after>package sign

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"

	gossh "github.com/coreos/fleet/third_party/code.google.com/p/gosshnew/ssh"
	gosshagent "github.com/coreos/fleet/third_party/code.google.com/p/gosshnew/ssh/agent"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/pkg"
	"github.com/coreos/fleet/ssh"
)

const DefaultAuthorizedKeysFile = "~/.ssh/authorized_keys"

// SignatureSet contains a set of SSH signatures for a blob of data, and is named by a Tag.
type SignatureSet struct {
	Tag        string
	Signatures []*gossh.Signature
}

// SignatureCreator provides the ability to sign a blob of data with multiple SSH public keys, contained in the keyring
type SignatureCreator struct {
	keyring gosshagent.Agent
}

// NewSignatureCreator instantiates a SignatureCreator with the given keyring
func NewSignatureCreator(keyring gosshagent.Agent) *SignatureCreator {
	return &SignatureCreator{keyring}
}

// NewSignatureCreatorFromSSHAgent return a SignatureCreator which uses the local ssh-agent as its keyring
func NewSignatureCreatorFromSSHAgent() (*SignatureCreator, error) {
	keyring, err := ssh.SSHAgentClient()
	if err != nil {
		return nil, err
	}
	return &SignatureCreator{keyring}, nil
}

// Sign generates a SignatureSet for the given data, labelled by the supplied tag. It returns a *SignatureSet and any error encountere
func (sc *SignatureCreator) Sign(tag string, data []byte) (*SignatureSet, error) {
	if sc.keyring == nil {
		return nil, errors.New("signature creator is uninitialized")
	}

	sigs := make([]*gossh.Signature, 0)

	keys, err := sc.keyring.List()
	if err != nil {
		return nil, err
	}
	if len(keys) == 0 {
		return nil, errors.New("signature creator keyring is empty")
	}

	for _, k := range keys {
		sig, err := sc.keyring.Sign(k, data)
		if err != nil {
			return nil, err
		}

		sigs = append(sigs, sig)
	}

	return &SignatureSet{tag, sigs}, nil
}

type SignatureVerifier struct {
	// keys is used to verify signing, created when needed
	pubkeys []gossh.PublicKey
}

func NewSignatureVerifier() *SignatureVerifier {
	return &SignatureVerifier{}
}

// NewSignatureVerifierFromSSHAgent return SignatureVerifier which uses public keys in the local ssh-agent to verify signatures
func NewSignatureVerifierFromSSHAgent() (*SignatureVerifier, error) {
	keyring, err := ssh.SSHAgentClient()
	if err != nil {
		return nil, err
	}
	return NewSignatureVerifierFromKeyring(keyring)
}

// NewSignatureVerifierFromKeyring creates a SignatureVerifier
// which uses public keys from the given keyring to verify signatures
func NewSignatureVerifierFromKeyring(keyring gosshagent.Agent) (*SignatureVerifier, error) {
	keys, err := keyring.List()
	if err != nil {
		return nil, err
	}

	pubkeys := make([]gossh.PublicKey, len(keys))

	for i, k := range keys {
		pubkeys[i] = k
	}

	return &SignatureVerifier{pubkeys}, nil
}

// NewSignatureVerifierFromAuthorizedKeysFile creates a
// SignatureVerifier which uses public keys from the specified
// authorized_keys file to verify signatures
func NewSignatureVerifierFromAuthorizedKeysFile(filepath string) (*SignatureVerifier, error) {
	out, err := ioutil.ReadFile(pkg.ParseFilepath(filepath))
	if err != nil {
		return nil, err
	}

	pubkeys, err := parseAuthorizedKeys(out)
	if err != nil {
		return nil, err
	}
	if len(pubkeys) == 0 {
		return nil, errors.New("no authorized keys found in file")
	}

	return &SignatureVerifier{pubkeys}, nil
}

// Verify verifies that at least one of the signatures in the provided
// SignatureSet is a valid signature of the given data blob.
func (sv *SignatureVerifier) Verify(data []byte, s *SignatureSet) (bool, error) {
	if sv.pubkeys == nil {
		return false, errors.New("signature verifier is uninitialized")
	}

	for _, authKey := range sv.pubkeys {
		// Manually coerce the keys provided by the agent to PublicKeys,
		// since gosshnew does not want to do it for us and agent.Keys are unable to Verify
		key, err := gossh.ParsePublicKey(authKey.Marshal())
		if err != nil {
			log.Errorf("Unable to use SSH key: %v", err)
			continue
		}

		for _, sign := range s.Signatures {
			if err := key.Verify(data, sign); err == nil {
				return true, nil
			}
		}
	}

	return false, nil
}

func parseAuthorizedKeys(in []byte) ([]gossh.PublicKey, error) {
	pubkeys := make([]gossh.PublicKey, 0)
	for len(in) > 0 {
		pubkey, _, _, rest, err := gossh.ParseAuthorizedKey(in)
		if err != nil {
			return nil, err
		}
		in = rest

		pubkeys = append(pubkeys, pubkey)
	}

	return pubkeys, nil
}

func marshal(obj interface{}) ([]byte, error) {
	encoded, err := json.Marshal(obj)
	if err == nil {
		return encoded, nil
	} else {
		return nil, errors.New(fmt.Sprintf("Unable to JSON-serialize object: %s", err))
	}
}
<|endoftext|>


<commit_before>package webrtc

import (
	"fmt"
	"strings"

	"net"

	"github.com/pions/webrtc/internal/sdp"
	"github.com/pkg/errors"
)

// RTCAnswerOptions describes the options used to control the answer creation process
type RTCAnswerOptions struct {
	VoiceActivityDetection bool
}

// RTCOfferOptions describes the options used to control the offer creation process
type RTCOfferOptions struct {
	VoiceActivityDetection bool
	ICERestart             bool
}

// RTCSignalingState indicates the state of the offer/answer process
type RTCSignalingState int

const (
	// RTCSignalingStateStable indicates there is no offeranswer exchange in progress.
	RTCSignalingStateStable RTCSignalingState = iota + 1

	// RTCSignalingStateHaveLocalOffer indicates A local description, of type "offer", has been successfully applied.
	RTCSignalingStateHaveLocalOffer

	// RTCSignalingStateHaveRemoteOffer indicates A remote description, of type "offer", has been successfully applied.
	RTCSignalingStateHaveRemoteOffer

	// RTCSignalingStateHaveLocalPranswer indicates A remote description of type "offer" has been successfully applied
	// and a local description of type "pranswer" has been successfully applied.
	RTCSignalingStateHaveLocalPranswer

	// RTCSignalingStateHaveRemotePranswer indicates A local description of type "offer" has been successfully applied
	// and a remote description of type "pranswer" has been successfully applied.
	RTCSignalingStateHaveRemotePranswer

	// RTCSignalingStateClosed indicates The RTCPeerConnection has been closed.
	RTCSignalingStateClosed
)

func (t RTCSignalingState) String() string {
	switch t {
	case RTCSignalingStateStable:
		return "stable"
	case RTCSignalingStateHaveLocalOffer:
		return "have-local-offer"
	case RTCSignalingStateHaveRemoteOffer:
		return "have-remote-offer"
	case RTCSignalingStateHaveLocalPranswer:
		return "have-local-pranswer"
	case RTCSignalingStateHaveRemotePranswer:
		return "have-remote-pranswer"
	case RTCSignalingStateClosed:
		return "closed"
	default:
		return ErrUnknownType.Error()
	}
}

// RTCSdpType describes the type of an RTCSessionDescription
type RTCSdpType int

const (
	// RTCSdpTypeOffer indicates that a description MUST be treated as an SDP offer.
	RTCSdpTypeOffer RTCSdpType = iota + 1

	// RTCSdpTypePranswer indicates that a description MUST be treated as an SDP answer, but not a final answer.
	RTCSdpTypePranswer

	// RTCSdpTypeAnswer indicates that a description MUST be treated as an SDP final answer, and the offer-answer
	// exchange MUST be considered complete.
	RTCSdpTypeAnswer

	// RTCSdpTypeRollback indicates that a description MUST be treated as canceling the current SDP negotiation
	// and moving the SDP offer and answer back to what it was in the previous stable state.
	RTCSdpTypeRollback
)

func (t RTCSdpType) String() string {
	switch t {
	case RTCSdpTypeOffer:
		return "offer"
	case RTCSdpTypePranswer:
		return "pranswer"
	case RTCSdpTypeAnswer:
		return "answer"
	case RTCSdpTypeRollback:
		return "rollback"
	default:
		return ErrUnknownType.Error()
	}
}

// RTCSessionDescription is used to expose local and remote session descriptions.
type RTCSessionDescription struct {
	Type RTCSdpType
	Sdp  string

	// This will never be initialized by callers, internal use only
	parsed *sdp.SessionDescription
}

// SetRemoteDescription sets the SessionDescription of the remote peer
func (r *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) error {
	if r.CurrentRemoteDescription != nil {
		return errors.Errorf("remoteDescription is already defined, SetRemoteDescription can only be called once")
	}

	weOffer := true
	remoteUfrag := ""
	remotePwd := ""
	if desc.Type == RTCSdpTypeOffer {
		weOffer = false
	}

	r.CurrentRemoteDescription = &desc
	r.CurrentRemoteDescription.parsed = &sdp.SessionDescription{}
	if err := r.CurrentRemoteDescription.parsed.Unmarshal(r.CurrentRemoteDescription.Sdp); err != nil {
		return err
	}

	for _, m := range r.CurrentRemoteDescription.parsed.MediaDescriptions {
		for _, a := range m.Attributes {
			if strings.HasPrefix(*a.String(), "candidate") {
				if c := sdp.ICECandidateUnmarshal(*a.String()); c != nil {
					r.networkManager.IceAgent.AddRemoteCandidate(c)
				} else {
					fmt.Printf("Tried to parse ICE candidate, but failed %s ", a)
				}
			} else if strings.HasPrefix(*a.String(), "ice-ufrag") {
				remoteUfrag = (*a.String())[len("ice-ufrag:"):]
			} else if strings.HasPrefix(*a.String(), "ice-pwd") {
				remotePwd = (*a.String())[len("ice-pwd:"):]
			}
		}
	}
	return r.networkManager.Start(weOffer, remoteUfrag, remotePwd)
}

func (r *RTCPeerConnection) generateLocalCandidates() []string {
	r.networkManager.IceAgent.RLock()
	defer r.networkManager.IceAgent.RUnlock()

	candidates := make([]string, 0)
	for _, c := range r.networkManager.IceAgent.LocalCandidates {
		candidates = append(candidates, sdp.ICECandidateMarshal(c)...)
	}
	return candidates
}

// CreateOffer starts the RTCPeerConnection and generates the localDescription
func (r *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDescription, error) {
	useIdentity := r.idpLoginURL != nil
	if options != nil {
		return RTCSessionDescription{}, errors.Errorf("TODO handle options")
	} else if useIdentity {
		return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
	} else if r.IsClosed {
		return RTCSessionDescription{}, &InvalidStateError{Err: ErrConnectionClosed}
	}

	d := sdp.NewJSEPSessionDescription(r.networkManager.DTLSFingerprint(), useIdentity)
	candidates := r.generateLocalCandidates()

	r.addRTPMediaSection(d, RTCRtpCodecTypeAudio, "audio", RTCRtpTransceiverDirectionSendrecv, candidates)
	r.addRTPMediaSection(d, RTCRtpCodecTypeVideo, "video", RTCRtpTransceiverDirectionSendrecv, candidates)
	r.addDataMediaSection(d, "data", candidates)
	d = d.WithValueAttribute(sdp.AttrKeyGroup, "BUNDLE audio video data")

	for _, m := range d.MediaDescriptions {
		m.WithPropertyAttribute("setup:actpass")
	}

	r.CurrentLocalDescription = &RTCSessionDescription{
		Type:   RTCSdpTypeOffer,
		Sdp:    d.Marshal(),
		parsed: d,
	}

	return *r.CurrentLocalDescription, nil
}

// CreateAnswer starts the RTCPeerConnection and generates the localDescription
func (r *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSessionDescription, error) {
	useIdentity := r.idpLoginURL != nil
	if options != nil {
		return RTCSessionDescription{}, errors.Errorf("TODO handle options")
	} else if useIdentity {
		return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
	} else if r.IsClosed {
		return RTCSessionDescription{}, &InvalidStateError{Err: ErrConnectionClosed}
	}

	candidates := r.generateLocalCandidates()
	d := sdp.NewJSEPSessionDescription(r.networkManager.DTLSFingerprint(), useIdentity)

	bundleValue := "BUNDLE"
	for _, remoteMedia := range r.CurrentRemoteDescription.parsed.MediaDescriptions {
		// TODO @trivigy better SDP parser
		var peerDirection RTCRtpTransceiverDirection
		midValue := ""

		for _, a := range remoteMedia.Attributes {
			if strings.HasPrefix(*a.String(), "mid") {
				midValue = (*a.String())[len("mid:"):]
			} else if strings.HasPrefix(*a.String(), "sendrecv") {
				peerDirection = RTCRtpTransceiverDirectionSendrecv
			} else if strings.HasPrefix(*a.String(), "sendonly") {
				peerDirection = RTCRtpTransceiverDirectionSendonly
			} else if strings.HasPrefix(*a.String(), "recvonly") {
				peerDirection = RTCRtpTransceiverDirectionRecvonly
			}
		}
		bundleValue += " " + midValue

		if strings.HasPrefix(*remoteMedia.MediaName.String(), "audio") {
			r.addRTPMediaSection(d, RTCRtpCodecTypeAudio, midValue, peerDirection, candidates)
		} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "video") {
			r.addRTPMediaSection(d, RTCRtpCodecTypeVideo, midValue, peerDirection, candidates)
		} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "application") {
			r.addDataMediaSection(d, midValue, candidates)
		}
	}

	d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)

	r.CurrentLocalDescription = &RTCSessionDescription{
		Type:   RTCSdpTypeAnswer,
		Sdp:    d.Marshal(),
		parsed: d,
	}
	return *r.CurrentLocalDescription, nil
}

func localDirection(weSend bool, peerDirection RTCRtpTransceiverDirection) RTCRtpTransceiverDirection {
	theySend := (peerDirection == RTCRtpTransceiverDirectionSendrecv || peerDirection == RTCRtpTransceiverDirectionSendonly)
	if weSend && theySend {
		return RTCRtpTransceiverDirectionSendrecv
	} else if weSend && !theySend {
		return RTCRtpTransceiverDirectionSendonly
	} else if !weSend && theySend {
		return RTCRtpTransceiverDirectionRecvonly
	}

	return RTCRtpTransceiverDirectionInactive
}

func (r *RTCPeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecType RTCRtpCodecType, midValue string, peerDirection RTCRtpTransceiverDirection, candidates []string) {
	media := sdp.NewJSEPMediaDescription(codecType.String(), []string{}).
		WithValueAttribute(sdp.AttrKeyConnectionSetup, sdp.ConnectionRoleActive.String()). // TODO: Support other connection types
		WithValueAttribute(sdp.AttrKeyMID, midValue).
		WithICECredentials(r.networkManager.IceAgent.LocalUfrag, r.networkManager.IceAgent.LocalPwd).
		WithPropertyAttribute(sdp.AttrKeyRtcpMux).  // TODO: support RTCP fallback
		WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP?

	for _, codec := range r.mediaEngine.getCodecsByKind(codecType) {
		media.WithCodec(codec.PayloadType, codec.Name, codec.ClockRate, codec.Channels, codec.SdpFmtpLine)
	}

	weSend := false
	for _, transceiver := range r.rtpTransceivers {
		if transceiver.Sender == nil ||
			transceiver.Sender.Track == nil ||
			transceiver.Sender.Track.Kind != codecType {
			continue
		}
		weSend = true
		track := transceiver.Sender.Track
		media = media.WithMediaSource(track.Ssrc, track.Label /* cname */, track.Label /* streamLabel */, track.Label)
	}
	media = media.WithPropertyAttribute(localDirection(weSend, peerDirection).String())

	for _, c := range candidates {
		media.WithCandidate(c)
	}
	media.WithPropertyAttribute("end-of-candidates")
	d.WithMedia(media)
}

func (r *RTCPeerConnection) addDataMediaSection(d *sdp.SessionDescription, midValue string, candidates []string) {
	media := (&sdp.MediaDescription{
		MediaName: sdp.MediaName{
			Media:   "application",
			Port:    sdp.RangedPort{Value: 9},
			Protos:  []string{"DTLS", "SCTP"},
			Formats: []int{5000},
		},
		ConnectionInformation: &sdp.ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address: &sdp.Address{
				IP: net.ParseIP("0.0.0.0"),
			},
		},
	}).
		WithValueAttribute(sdp.AttrKeyConnectionSetup, sdp.ConnectionRoleActive.String()). // TODO: Support other connection types
		WithValueAttribute(sdp.AttrKeyMID, midValue).
		WithPropertyAttribute(RTCRtpTransceiverDirectionSendrecv.String()).
		WithPropertyAttribute("sctpmap:5000 webrtc-datachannel 1024").
		WithICECredentials(r.networkManager.IceAgent.LocalUfrag, r.networkManager.IceAgent.LocalPwd)

	for _, c := range candidates {
		media.WithCandidate(c)
	}
	media.WithPropertyAttribute("end-of-candidates")

	d.WithMedia(media)
}
<commit_msg>Fix merge conflicts that broke setting AttrKeyConnectionSetup<commit_after>package webrtc

import (
	"fmt"
	"strings"

	"net"

	"github.com/pions/webrtc/internal/sdp"
	"github.com/pkg/errors"
)

// RTCAnswerOptions describes the options used to control the answer creation process
type RTCAnswerOptions struct {
	VoiceActivityDetection bool
}

// RTCOfferOptions describes the options used to control the offer creation process
type RTCOfferOptions struct {
	VoiceActivityDetection bool
	ICERestart             bool
}

// RTCSignalingState indicates the state of the offer/answer process
type RTCSignalingState int

const (
	// RTCSignalingStateStable indicates there is no offeranswer exchange in progress.
	RTCSignalingStateStable RTCSignalingState = iota + 1

	// RTCSignalingStateHaveLocalOffer indicates A local description, of type "offer", has been successfully applied.
	RTCSignalingStateHaveLocalOffer

	// RTCSignalingStateHaveRemoteOffer indicates A remote description, of type "offer", has been successfully applied.
	RTCSignalingStateHaveRemoteOffer

	// RTCSignalingStateHaveLocalPranswer indicates A remote description of type "offer" has been successfully applied
	// and a local description of type "pranswer" has been successfully applied.
	RTCSignalingStateHaveLocalPranswer

	// RTCSignalingStateHaveRemotePranswer indicates A local description of type "offer" has been successfully applied
	// and a remote description of type "pranswer" has been successfully applied.
	RTCSignalingStateHaveRemotePranswer

	// RTCSignalingStateClosed indicates The RTCPeerConnection has been closed.
	RTCSignalingStateClosed
)

func (t RTCSignalingState) String() string {
	switch t {
	case RTCSignalingStateStable:
		return "stable"
	case RTCSignalingStateHaveLocalOffer:
		return "have-local-offer"
	case RTCSignalingStateHaveRemoteOffer:
		return "have-remote-offer"
	case RTCSignalingStateHaveLocalPranswer:
		return "have-local-pranswer"
	case RTCSignalingStateHaveRemotePranswer:
		return "have-remote-pranswer"
	case RTCSignalingStateClosed:
		return "closed"
	default:
		return ErrUnknownType.Error()
	}
}

// RTCSdpType describes the type of an RTCSessionDescription
type RTCSdpType int

const (
	// RTCSdpTypeOffer indicates that a description MUST be treated as an SDP offer.
	RTCSdpTypeOffer RTCSdpType = iota + 1

	// RTCSdpTypePranswer indicates that a description MUST be treated as an SDP answer, but not a final answer.
	RTCSdpTypePranswer

	// RTCSdpTypeAnswer indicates that a description MUST be treated as an SDP final answer, and the offer-answer
	// exchange MUST be considered complete.
	RTCSdpTypeAnswer

	// RTCSdpTypeRollback indicates that a description MUST be treated as canceling the current SDP negotiation
	// and moving the SDP offer and answer back to what it was in the previous stable state.
	RTCSdpTypeRollback
)

func (t RTCSdpType) String() string {
	switch t {
	case RTCSdpTypeOffer:
		return "offer"
	case RTCSdpTypePranswer:
		return "pranswer"
	case RTCSdpTypeAnswer:
		return "answer"
	case RTCSdpTypeRollback:
		return "rollback"
	default:
		return ErrUnknownType.Error()
	}
}

// RTCSessionDescription is used to expose local and remote session descriptions.
type RTCSessionDescription struct {
	Type RTCSdpType
	Sdp  string

	// This will never be initialized by callers, internal use only
	parsed *sdp.SessionDescription
}

// SetRemoteDescription sets the SessionDescription of the remote peer
func (r *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) error {
	if r.CurrentRemoteDescription != nil {
		return errors.Errorf("remoteDescription is already defined, SetRemoteDescription can only be called once")
	}

	weOffer := true
	remoteUfrag := ""
	remotePwd := ""
	if desc.Type == RTCSdpTypeOffer {
		weOffer = false
	}

	r.CurrentRemoteDescription = &desc
	r.CurrentRemoteDescription.parsed = &sdp.SessionDescription{}
	if err := r.CurrentRemoteDescription.parsed.Unmarshal(r.CurrentRemoteDescription.Sdp); err != nil {
		return err
	}

	for _, m := range r.CurrentRemoteDescription.parsed.MediaDescriptions {
		for _, a := range m.Attributes {
			if strings.HasPrefix(*a.String(), "candidate") {
				if c := sdp.ICECandidateUnmarshal(*a.String()); c != nil {
					r.networkManager.IceAgent.AddRemoteCandidate(c)
				} else {
					fmt.Printf("Tried to parse ICE candidate, but failed %s ", a)
				}
			} else if strings.HasPrefix(*a.String(), "ice-ufrag") {
				remoteUfrag = (*a.String())[len("ice-ufrag:"):]
			} else if strings.HasPrefix(*a.String(), "ice-pwd") {
				remotePwd = (*a.String())[len("ice-pwd:"):]
			}
		}
	}
	return r.networkManager.Start(weOffer, remoteUfrag, remotePwd)
}

func (r *RTCPeerConnection) generateLocalCandidates() []string {
	r.networkManager.IceAgent.RLock()
	defer r.networkManager.IceAgent.RUnlock()

	candidates := make([]string, 0)
	for _, c := range r.networkManager.IceAgent.LocalCandidates {
		candidates = append(candidates, sdp.ICECandidateMarshal(c)...)
	}
	return candidates
}

// CreateOffer starts the RTCPeerConnection and generates the localDescription
func (r *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDescription, error) {
	useIdentity := r.idpLoginURL != nil
	if options != nil {
		return RTCSessionDescription{}, errors.Errorf("TODO handle options")
	} else if useIdentity {
		return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
	} else if r.IsClosed {
		return RTCSessionDescription{}, &InvalidStateError{Err: ErrConnectionClosed}
	}

	d := sdp.NewJSEPSessionDescription(r.networkManager.DTLSFingerprint(), useIdentity)
	candidates := r.generateLocalCandidates()

	r.addRTPMediaSection(d, RTCRtpCodecTypeAudio, "audio", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass)
	r.addRTPMediaSection(d, RTCRtpCodecTypeVideo, "video", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass)
	r.addDataMediaSection(d, "data", candidates, sdp.ConnectionRoleActpass)
	d = d.WithValueAttribute(sdp.AttrKeyGroup, "BUNDLE audio video data")

	for _, m := range d.MediaDescriptions {
		m.WithPropertyAttribute("setup:actpass")
	}

	r.CurrentLocalDescription = &RTCSessionDescription{
		Type:   RTCSdpTypeOffer,
		Sdp:    d.Marshal(),
		parsed: d,
	}

	return *r.CurrentLocalDescription, nil
}

// CreateAnswer starts the RTCPeerConnection and generates the localDescription
func (r *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSessionDescription, error) {
	useIdentity := r.idpLoginURL != nil
	if options != nil {
		return RTCSessionDescription{}, errors.Errorf("TODO handle options")
	} else if useIdentity {
		return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
	} else if r.IsClosed {
		return RTCSessionDescription{}, &InvalidStateError{Err: ErrConnectionClosed}
	}

	candidates := r.generateLocalCandidates()
	d := sdp.NewJSEPSessionDescription(r.networkManager.DTLSFingerprint(), useIdentity)

	bundleValue := "BUNDLE"
	for _, remoteMedia := range r.CurrentRemoteDescription.parsed.MediaDescriptions {
		// TODO @trivigy better SDP parser
		var peerDirection RTCRtpTransceiverDirection
		midValue := ""

		for _, a := range remoteMedia.Attributes {
			if strings.HasPrefix(*a.String(), "mid") {
				midValue = (*a.String())[len("mid:"):]
			} else if strings.HasPrefix(*a.String(), "sendrecv") {
				peerDirection = RTCRtpTransceiverDirectionSendrecv
			} else if strings.HasPrefix(*a.String(), "sendonly") {
				peerDirection = RTCRtpTransceiverDirectionSendonly
			} else if strings.HasPrefix(*a.String(), "recvonly") {
				peerDirection = RTCRtpTransceiverDirectionRecvonly
			}
		}
		bundleValue += " " + midValue

		if strings.HasPrefix(*remoteMedia.MediaName.String(), "audio") {
			r.addRTPMediaSection(d, RTCRtpCodecTypeAudio, midValue, peerDirection, candidates, sdp.ConnectionRoleActive)
		} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "video") {
			r.addRTPMediaSection(d, RTCRtpCodecTypeVideo, midValue, peerDirection, candidates, sdp.ConnectionRoleActive)
		} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "application") {
			r.addDataMediaSection(d, midValue, candidates, sdp.ConnectionRoleActive)
		}
	}

	d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)

	r.CurrentLocalDescription = &RTCSessionDescription{
		Type:   RTCSdpTypeAnswer,
		Sdp:    d.Marshal(),
		parsed: d,
	}
	return *r.CurrentLocalDescription, nil
}

func localDirection(weSend bool, peerDirection RTCRtpTransceiverDirection) RTCRtpTransceiverDirection {
	theySend := (peerDirection == RTCRtpTransceiverDirectionSendrecv || peerDirection == RTCRtpTransceiverDirectionSendonly)
	if weSend && theySend {
		return RTCRtpTransceiverDirectionSendrecv
	} else if weSend && !theySend {
		return RTCRtpTransceiverDirectionSendonly
	} else if !weSend && theySend {
		return RTCRtpTransceiverDirectionRecvonly
	}

	return RTCRtpTransceiverDirectionInactive
}

func (r *RTCPeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecType RTCRtpCodecType, midValue string, peerDirection RTCRtpTransceiverDirection, candidates []string, dtlsRole sdp.ConnectionRole) {
	if codecs := r.mediaEngine.getCodecsByKind(codecType); len(codecs) == 0 {
		return
	}

	media := sdp.NewJSEPMediaDescription(codecType.String(), []string{}).
		WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
		WithValueAttribute(sdp.AttrKeyMID, midValue).
		WithICECredentials(r.networkManager.IceAgent.LocalUfrag, r.networkManager.IceAgent.LocalPwd).
		WithPropertyAttribute(sdp.AttrKeyRtcpMux).  // TODO: support RTCP fallback
		WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP?

	for _, codec := range r.mediaEngine.getCodecsByKind(codecType) {
		media.WithCodec(codec.PayloadType, codec.Name, codec.ClockRate, codec.Channels, codec.SdpFmtpLine)
	}

	weSend := false
	for _, transceiver := range r.rtpTransceivers {
		if transceiver.Sender == nil ||
			transceiver.Sender.Track == nil ||
			transceiver.Sender.Track.Kind != codecType {
			continue
		}
		weSend = true
		track := transceiver.Sender.Track
		media = media.WithMediaSource(track.Ssrc, track.Label /* cname */, track.Label /* streamLabel */, track.Label)
	}
	media = media.WithPropertyAttribute(localDirection(weSend, peerDirection).String())

	for _, c := range candidates {
		media.WithCandidate(c)
	}
	media.WithPropertyAttribute("end-of-candidates")
	d.WithMedia(media)
}

func (r *RTCPeerConnection) addDataMediaSection(d *sdp.SessionDescription, midValue string, candidates []string, dtlsRole sdp.ConnectionRole) {
	media := (&sdp.MediaDescription{
		MediaName: sdp.MediaName{
			Media:   "application",
			Port:    sdp.RangedPort{Value: 9},
			Protos:  []string{"DTLS", "SCTP"},
			Formats: []int{5000},
		},
		ConnectionInformation: &sdp.ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address: &sdp.Address{
				IP: net.ParseIP("0.0.0.0"),
			},
		},
	}).
		WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). // TODO: Support other connection types
		WithValueAttribute(sdp.AttrKeyMID, midValue).
		WithPropertyAttribute(RTCRtpTransceiverDirectionSendrecv.String()).
		WithPropertyAttribute("sctpmap:5000 webrtc-datachannel 1024").
		WithICECredentials(r.networkManager.IceAgent.LocalUfrag, r.networkManager.IceAgent.LocalPwd)

	for _, c := range candidates {
		media.WithCandidate(c)
	}
	media.WithPropertyAttribute("end-of-candidates")

	d.WithMedia(media)
}
<|endoftext|>


<commit_before>package mmm

import (
	"testing"
	"unsafe"
)

// -----------------------------------------------------------------------------

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

func TestSize_SizeOf_uint8(t *testing.T) {
	var v uint8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint8")
	}
}

func TestSize_SizeOf_uint16(t *testing.T) {
	var v uint16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint16")
	}
}

func TestSize_SizeOf_uint32(t *testing.T) {
	var v uint32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint32")
	}
}

func TestSize_SizeOf_uint64(t *testing.T) {
	var v uint64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint64")
	}
}

func TestSize_SizeOf_uintptr(t *testing.T) {
	var v uintptr
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uintptr")
	}
}

func TestSize_SizeOf_float32(t *testing.T) {
	var v float32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for float32")
	}
}

func TestSize_SizeOf_float64(t *testing.T) {
	var v float64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for float64")
	}
}

func TestSize_SizeOf_complex64(t *testing.T) {
	var v complex64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for complex64")
	}
}

func TestSize_SizeOf_complex128(t *testing.T) {
	var v complex128
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for complex128")
	}
}

func TestSize_SizeOf_int_array(t *testing.T) {
	var v [42]int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for [42]int")
	}
}

//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<commit_msg>added TestSize_SizeOf_int_chan<commit_after>package mmm

import (
	"testing"
	"unsafe"
)

// -----------------------------------------------------------------------------

func TestSize_SizeOf_bool(t *testing.T) {
	var v bool
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for bool")
	}
}

func TestSize_SizeOf_int(t *testing.T) {
	var v int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int")
	}
}

func TestSize_SizeOf_int8(t *testing.T) {
	var v int8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int8")
	}
}

func TestSize_SizeOf_int16(t *testing.T) {
	var v int16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int16")
	}
}

func TestSize_SizeOf_int32(t *testing.T) {
	var v int32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int32")
	}
}

func TestSize_SizeOf_int64(t *testing.T) {
	var v int64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for int64")
	}
}

func TestSize_SizeOf_uint(t *testing.T) {
	var v uint
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint")
	}
}

func TestSize_SizeOf_uint8(t *testing.T) {
	var v uint8
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint8")
	}
}

func TestSize_SizeOf_uint16(t *testing.T) {
	var v uint16
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint16")
	}
}

func TestSize_SizeOf_uint32(t *testing.T) {
	var v uint32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint32")
	}
}

func TestSize_SizeOf_uint64(t *testing.T) {
	var v uint64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uint64")
	}
}

func TestSize_SizeOf_uintptr(t *testing.T) {
	var v uintptr
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for uintptr")
	}
}

func TestSize_SizeOf_float32(t *testing.T) {
	var v float32
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for float32")
	}
}

func TestSize_SizeOf_float64(t *testing.T) {
	var v float64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for float64")
	}
}

func TestSize_SizeOf_complex64(t *testing.T) {
	var v complex64
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for complex64")
	}
}

func TestSize_SizeOf_complex128(t *testing.T) {
	var v complex128
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for complex128")
	}
}

func TestSize_SizeOf_int_array(t *testing.T) {
	var v [42]int
	size, err := SizeOf(v)
	if err != nil {
		t.Error(err)
	}
	if size != unsafe.Sizeof(v) {
		t.Error("invalid size for [42]int")
	}
}

func TestSize_SizeOf_int_chan(t *testing.T) {
	var v chan int
	_, err := SizeOf(v)
	if err == nil {
		t.Error("should not be supported")
		if _, ok := err.(Error); !ok {
			t.Error("should have mmm.Error")
		}
	}
}

//var ichan chan int
//var itf interface{}
//var imap map[int]int
//var iptr *int
//var islice []int
//var str string
//var srt struct{}
<|endoftext|>


<commit_before>package irckit

import (
	"strings"
	"time"

	"github.com/nlopes/slack"
)

type SlackInfo struct {
	Token  string
	sc     *slack.Client
	rtm    *slack.RTM
	sinfo  *slack.Info
	susers map[string]slack.User
}

func (u *User) loginToSlack() (*slack.Client, error) {
	u.sc = slack.New(u.Token)
	u.rtm = u.sc.NewRTM()
	u.susers = make(map[string]slack.User)
	go u.rtm.ManageConnection()
	go u.handleSlack()
	time.Sleep(time.Second * 2)
	u.sinfo = u.rtm.GetInfo()
	u.addSlackUsersToChannels()
	return u.sc, nil
}

func (u *User) logoutFromSlack() error {
	logger.Debug("calling logout from slack")
	err := u.rtm.Disconnect()
	if err != nil {
		logger.Debug("logoutfrom slack", err)
		return err
	}
	u.Srv.Logout(u)
	u.sc = nil
	logger.Info("logout succeeded")
	return nil
}

func (u *User) createSlackUser(slackuser *slack.User) *User {
	if slackuser == nil {
		return nil
	}
	if ghost, ok := u.Srv.HasUser(slackuser.Name); ok {
		return ghost
	}
	ghost := &User{Nick: slackuser.Name, User: slackuser.ID, Real: slackuser.RealName, Host: "host", Roles: "", channels: map[Channel]struct{}{}}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	return ghost
}

func (u *User) addSlackUserToChannel(user *slack.User, channel string, channelId string) {
	if user == nil {
		return
	}
	ghost := u.createSlackUser(user)
	if ghost == nil {
		logger.Warnf("Cannot join %v into %s", user, channel)
		return
	}
	logger.Debugf("adding %s to %s (%s)", ghost.Nick, channel, channelId)
	ch := u.Srv.Channel(channelId)
	logger.Debugf("channel: %#v %#v", ch.String(), ch.ID())
	ch.Join(ghost)
}

func (u *User) addSlackUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 100)
	logger.Debug("in addUsersToChannels()")
	// add all users, also who are not on channels
	ch := srv.Channel("&users")
	users, _ := u.sc.GetUsers()
	for _, mmuser := range users {
		// do not add our own nick
		if mmuser.ID == u.sinfo.User.ID {
			continue
		}
		u.createSlackUser(&mmuser)
		u.addSlackUserToChannel(&mmuser, "&users", "&users")
		u.susers[mmuser.ID] = mmuser
	}
	ch.Join(u)

	channels := make(chan slack.Channel, 10)
	for i := 0; i < 10; i++ {
		go u.addSlackUserToChannelWorker(channels, throttle)
	}
	mmchannels, _ := u.sc.GetChannels(true)
	for _, mmchannel := range mmchannels {
		if mmchannel.IsMember {
			logger.Debug("Adding channel", mmchannel)
			channels <- mmchannel
		}
	}
	close(channels)
}

func (u *User) addSlackUserToChannelWorker(channels <-chan slack.Channel, throttle <-chan time.Time) {
	for {
		mmchannel, ok := <-channels
		if !ok {
			logger.Debug("Done adding user to channels")
			return
		}
		<-throttle
		// exclude direct messages
		//var spoof func(string, string)
		u.syncSlackChannel(mmchannel.ID, mmchannel.Name)
		//ch := u.Srv.Channel(mmchannel.ID)
		// post everything to the channel you haven't seen yet
	}
}

func (u *User) handleSlack() {
	for {
		/*
			if u.mc.WsQuit {
				logger.Debug("exiting handleWsMessage")
				return
			}
		*/
		logger.Debug("in handleSlack")
		for msg := range u.rtm.IncomingEvents {
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				u.handleSlackActionPost(ev)
			case *slack.DisconnectedEvent:
				logger.Debugf("disconnecting..")
				return
			}
		}
	}
	/*
			logger.Debugf("MMUser WsReceiver: %#v", message.Raw)
			// check if we have the users/channels in our cache. If not update
			u.checkWsActionMessage(message.Raw, updateChannelsThrottle)
			switch message.Raw.Event {
			case model.WEBSOCKET_EVENT_POSTED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_POST_EDITED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_USER_REMOVED:
				u.handleWsActionUserRemoved(message.Raw)
			case model.WEBSOCKET_EVENT_USER_ADDED:
				u.handleWsActionUserAdded(message.Raw)
			}
		}
	*/
}

func (u *User) handleSlackActionPost(rmsg *slack.MessageEvent) {
	var ch Channel
	logger.Debugf("handleSlackActionPost() receiving msg %#v", rmsg)
	if len(rmsg.Attachments) > 0 {
		// skip messages we made ourselves
		if rmsg.Attachments[0].CallbackID == "matterircd" {
			return
		}
	}

	user, err := u.rtm.GetUserInfo(rmsg.User)
	if err != nil {
		return
	}

	// create new "ghost" user
	ghost := u.createSlackUser(user)

	spoofUsername := user.ID
	if ghost != nil {
		spoofUsername = ghost.Nick
	}

	msgs := strings.Split(rmsg.Text, "\n")
	// direct message

	if ghost != nil {
		ch = u.Srv.Channel(rmsg.Channel)
		// join if not in channel
		if !ch.HasUser(ghost) {
			ch.Join(ghost)
		}
	}

	for _, m := range msgs {
		if m == "" {
			continue
		}
		if strings.HasPrefix(rmsg.Channel, "D") {
			u.MsgSpoofUser(spoofUsername, m)
		} else {
			ch.SpoofMessage(spoofUsername, m)
		}
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackChannel(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetChannelInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			if slackuser, ok := u.susers[user]; ok {
				u.addSlackUserToChannel(&slackuser, "#"+name, id)
			}
		}
	}
	// before joining ourself
	for _, user := range info.Members {
		// join all the channels we're on on MM
		if user == u.sinfo.User.ID {
			ch := srv.Channel(id)
			// only join when we're not yet on the channel
			if !ch.HasUser(u) {
				logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
				ch.Join(u)
				//ch.Topic(u, u.mc.GetChannelHeader(id))
			}
			break
		}
	}
}
<commit_msg>Fix possible panic on nil sinfo. Closes 42wim/matterircd#145<commit_after>package irckit

import (
	"strings"
	"time"

	"github.com/nlopes/slack"
)

type SlackInfo struct {
	Token  string
	sc     *slack.Client
	rtm    *slack.RTM
	sinfo  *slack.Info
	susers map[string]slack.User
}

func (u *User) loginToSlack() (*slack.Client, error) {
	u.sc = slack.New(u.Token)
	u.rtm = u.sc.NewRTM()
	u.susers = make(map[string]slack.User)
	go u.rtm.ManageConnection()
	go u.handleSlack()
	//time.Sleep(time.Second * 2)
	u.sinfo = u.rtm.GetInfo()
	for u.sinfo == nil {
		time.Sleep(time.Millisecond * 500)
		logger.Debug("still waiting for sinfo")
		u.sinfo = u.rtm.GetInfo()
	}
	u.addSlackUsersToChannels()
	return u.sc, nil
}

func (u *User) logoutFromSlack() error {
	logger.Debug("calling logout from slack")
	err := u.rtm.Disconnect()
	if err != nil {
		logger.Debug("logoutfrom slack", err)
		return err
	}
	u.Srv.Logout(u)
	u.sc = nil
	logger.Info("logout succeeded")
	return nil
}

func (u *User) createSlackUser(slackuser *slack.User) *User {
	if slackuser == nil {
		return nil
	}
	if ghost, ok := u.Srv.HasUser(slackuser.Name); ok {
		return ghost
	}
	ghost := &User{Nick: slackuser.Name, User: slackuser.ID, Real: slackuser.RealName, Host: "host", Roles: "", channels: map[Channel]struct{}{}}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	return ghost
}

func (u *User) addSlackUserToChannel(user *slack.User, channel string, channelId string) {
	if user == nil {
		return
	}
	ghost := u.createSlackUser(user)
	if ghost == nil {
		logger.Warnf("Cannot join %v into %s", user, channel)
		return
	}
	logger.Debugf("adding %s to %s (%s)", ghost.Nick, channel, channelId)
	ch := u.Srv.Channel(channelId)
	logger.Debugf("channel: %#v %#v", ch.String(), ch.ID())
	ch.Join(ghost)
}

func (u *User) addSlackUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 100)
	logger.Debug("in addUsersToChannels()")
	// add all users, also who are not on channels
	ch := srv.Channel("&users")
	users, _ := u.sc.GetUsers()
	for _, mmuser := range users {
		// do not add our own nick
		if mmuser.ID == u.sinfo.User.ID {
			continue
		}
		u.createSlackUser(&mmuser)
		u.addSlackUserToChannel(&mmuser, "&users", "&users")
		u.susers[mmuser.ID] = mmuser
	}
	ch.Join(u)

	channels := make(chan slack.Channel, 10)
	for i := 0; i < 10; i++ {
		go u.addSlackUserToChannelWorker(channels, throttle)
	}
	mmchannels, _ := u.sc.GetChannels(true)
	for _, mmchannel := range mmchannels {
		if mmchannel.IsMember {
			logger.Debug("Adding channel", mmchannel)
			channels <- mmchannel
		}
	}
	close(channels)
}

func (u *User) addSlackUserToChannelWorker(channels <-chan slack.Channel, throttle <-chan time.Time) {
	for {
		mmchannel, ok := <-channels
		if !ok {
			logger.Debug("Done adding user to channels")
			return
		}
		<-throttle
		// exclude direct messages
		//var spoof func(string, string)
		u.syncSlackChannel(mmchannel.ID, mmchannel.Name)
		//ch := u.Srv.Channel(mmchannel.ID)
		// post everything to the channel you haven't seen yet
	}
}

func (u *User) handleSlack() {
	for {
		/*
			if u.mc.WsQuit {
				logger.Debug("exiting handleWsMessage")
				return
			}
		*/
		logger.Debug("in handleSlack")
		for msg := range u.rtm.IncomingEvents {
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				u.handleSlackActionPost(ev)
			case *slack.DisconnectedEvent:
				logger.Debugf("disconnecting..")
				return
			}
		}
	}
	/*
			logger.Debugf("MMUser WsReceiver: %#v", message.Raw)
			// check if we have the users/channels in our cache. If not update
			u.checkWsActionMessage(message.Raw, updateChannelsThrottle)
			switch message.Raw.Event {
			case model.WEBSOCKET_EVENT_POSTED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_POST_EDITED:
				u.handleWsActionPost(message.Raw)
			case model.WEBSOCKET_EVENT_USER_REMOVED:
				u.handleWsActionUserRemoved(message.Raw)
			case model.WEBSOCKET_EVENT_USER_ADDED:
				u.handleWsActionUserAdded(message.Raw)
			}
		}
	*/
}

func (u *User) handleSlackActionPost(rmsg *slack.MessageEvent) {
	var ch Channel
	logger.Debugf("handleSlackActionPost() receiving msg %#v", rmsg)
	if len(rmsg.Attachments) > 0 {
		// skip messages we made ourselves
		if rmsg.Attachments[0].CallbackID == "matterircd" {
			return
		}
	}

	user, err := u.rtm.GetUserInfo(rmsg.User)
	if err != nil {
		return
	}

	// create new "ghost" user
	ghost := u.createSlackUser(user)

	spoofUsername := user.ID
	if ghost != nil {
		spoofUsername = ghost.Nick
	}

	msgs := strings.Split(rmsg.Text, "\n")
	// direct message

	if ghost != nil {
		ch = u.Srv.Channel(rmsg.Channel)
		// join if not in channel
		if !ch.HasUser(ghost) {
			ch.Join(ghost)
		}
	}

	for _, m := range msgs {
		if m == "" {
			continue
		}
		if strings.HasPrefix(rmsg.Channel, "D") {
			u.MsgSpoofUser(spoofUsername, m)
		} else {
			ch.SpoofMessage(spoofUsername, m)
		}
	}
}

// sync IRC with mattermost channel state
func (u *User) syncSlackChannel(id string, name string) {
	srv := u.Srv
	info, err := u.sc.GetChannelInfo(id)
	if err != nil {
		logger.Info(err)
	}

	for _, user := range info.Members {
		if u.sinfo.User.ID != user {
			//slackuser, _ := u.sc.GetUserInfo(user)
			if slackuser, ok := u.susers[user]; ok {
				u.addSlackUserToChannel(&slackuser, "#"+name, id)
			}
		}
	}
	// before joining ourself
	for _, user := range info.Members {
		// join all the channels we're on on MM
		if user == u.sinfo.User.ID {
			ch := srv.Channel(id)
			// only join when we're not yet on the channel
			if !ch.HasUser(u) {
				logger.Debugf("syncSlackchannel adding myself to %s (id: %s)", name, id)
				ch.Join(u)
				//ch.Topic(u, u.mc.GetChannelHeader(id))
			}
			break
		}
	}
}
<|endoftext|>