<commit_before>// Package directEmail support direct send email from selected interface include SOCKS5 proxy server
package directEmail

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"golang.org/x/net/proxy"
	"net"
	"net/smtp"
	"net/url"
	"strings"
	"time"
)

// A Email contains options for send email.
type Email struct {
	// Ip contains local IP address which use for send email
	// if blank use default interface
	// if use socks5 proxy "socks://123.124.125.126:8080"
	// and with auth "socks://user:password@123.124.125.126:8080"
	Ip string
	// Host is host name
	// if blank use DNS resolv for field fill
	Host string
	// Port SMTP server port
	Port uint16
	// MapIp use for translate local IP to global if NAT
	// if use Socks server translate IP SOCKS server to real IP
	MapIp map[string]string
	// FromEmail sender email (Required)
	FromEmail string
	// FromName sender name
	FromName string
	// ToEmail recipient email (Required)
	ToEmail string
	// ToName recipient name
	ToName string
	// Subject email subject
	Subject string

	headers     []string
	textPlain   []byte
	textHTML    []byte
	attachments [][]byte
	raw         bytes.Buffer
	bodyLenght  int
}

const debugIs = false

// New returns a new Email instance for create and send email
func New() Email {
	return Email{Port: 25}
}

// SendThroughServer send email from SMTP server
func (slf *Email) SendThroughServer(host string, port uint16, username, password string) error {
	slf.Port = port

	dialFunc, err := slf.dialFunction()
	debug("Dialer selected, now dial to server\n")

	conn, err := dialFunc("tcp", net.JoinHostPort(host, fmt.Sprintf("%d", slf.Port)))
	if err != nil {
		debug("Not connected\n")
		return err
	}
	debug("Connected\n")

	client, err := smtp.NewClient(conn, host)
	if err != nil {
		return err
	}

	auth := smtp.PlainAuth(
		"",
		username,
		password,
		host,
	)
	slf.Host = "localhost"

	debug("Connected, now send email\n")
	return slf.send(auth, host, client)
}

// Send email directly
func (slf *Email) Send() error {
	var err error

	slf.cleanEmail()

	server, err := slf.domainFromEmail(slf.ToEmail)
	if err != nil {
		return errors.New("553 Bad ToEmail")
	}

	dialFunc, err := slf.dialFunction()
	if err != nil {
		return fmt.Errorf("421 %v", err)
	}

	client, err := slf.newClient(server, dialFunc)
	if err != nil {
		return fmt.Errorf("421 %v", err)
	}
	defer client.Close()

	return slf.send(nil, "", client)
}

func (slf *Email) cleanEmail() {
	slf.ToEmail = strings.TrimSpace(slf.ToEmail)
	slf.FromEmail = strings.TrimSpace(slf.FromEmail)
}

var testHookStartTLS func(*tls.Config)

// Send sending email message
func (slf *Email) send(auth smtp.Auth, host string, client *smtp.Client) error {
	var err error

	if err := client.Hello(strings.TrimRight(slf.Host, ".")); err != nil {
		return err
	}

	if auth != nil {
		if ok, _ := client.Extension("STARTTLS"); ok {
			config := &tls.Config{ServerName: host}
			if testHookStartTLS != nil {
				testHookStartTLS(config)
			}
			if err = client.StartTLS(config); err != nil {
				return err
			}
		}
		if auth != nil {
			if err = client.Auth(auth); err != nil {
				return err
			}
		}
	}

	if err := client.Mail(slf.FromEmail); err != nil {
		return err
	}

	if err := client.Rcpt(slf.ToEmail); err != nil {
		return err
	}

	w, err := client.Data()
	if err != nil {
		return err
	}

	_, err = fmt.Fprint(w, slf.raw.String())
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	return client.Quit()

}

type conn func(network, address string) (net.Conn, error)

func (slf *Email) dialFunction() (conn, error) {
	var dialFunc conn

	if slf.Ip == "" {
		iface := net.Dialer{}
		dialFunc = iface.Dial
		debug("Dial function is default network interface\n")
	} else {
		if strings.ToLower(slf.Ip[0:8]) == "socks://" {
			u, err := url.Parse(slf.Ip)
			if err != nil {
				return nil, fmt.Errorf("Error parse socks: %s", err.Error())
			}
			var iface proxy.Dialer
			if u.User != nil {
				auth := proxy.Auth{}
				auth.User = u.User.Username()
				auth.Password, _ = u.User.Password()
				iface, err = proxy.SOCKS5("tcp", u.Host, &auth, proxy.FromEnvironment())
				if err != nil {
					return dialFunc, err
				}
			} else {
				iface, err = proxy.SOCKS5("tcp", u.Host, nil, proxy.FromEnvironment())
				if err != nil {
					return dialFunc, err
				}
			}
			slf.Ip = u.Host
			dialFunc = iface.Dial
			debug("Dial function is socks proxy from ", slf.Ip[8:], "\n")
		} else {
			addr := &net.TCPAddr{
				IP: net.ParseIP(slf.Ip),
			}
			iface := net.Dialer{LocalAddr: addr}
			dialFunc = iface.Dial
			debug("Dial function is ", addr.String(), " network interface\n")
		}
	}

	return dialFunc, nil
}

func (slf *Email) newClient(server string, dialFunc conn) (client *smtp.Client, err error) {
	var conn net.Conn

	records, err := net.LookupMX(server)
	if err != nil {
		return
	}
	debug("MX for domain:\n")
	for i := range records {
		debug(" - ", records[i].Pref, " ", records[i].Host, "\n")
	}

	for i := range records {
		server := strings.TrimRight(strings.TrimSpace(records[i].Host), ".")
		debug("Connect to server ", server, "\n")
		conn, err = dialFunc("tcp", net.JoinHostPort(server, fmt.Sprintf("%d", slf.Port)))
		if err != nil {
			debug("Not connected\n")
			continue
		}
		debug("Connected\n")
		client, err = smtp.NewClient(conn, server)
		if err == nil {
			break
		}
	}
	if err != nil {
		return
	}

	conn.SetDeadline(time.Now().Add(5 * time.Minute)) // SMTP RFC

	if slf.Ip == "" {
		slf.Ip = conn.LocalAddr().String()
	}

	if slf.Host == "" {
		var myGlobalIP string
		myIP, _, err := net.SplitHostPort(strings.TrimLeft(slf.Ip, "socks://"))
		myGlobalIP, ok := slf.MapIp[myIP]
		if !ok {
			myGlobalIP = myIP
		}
		names, err := net.LookupAddr(myGlobalIP)
		if err != nil && len(names) < 1 {
			return nil, err
		}
		debug("LookUp ", myGlobalIP, " this result ", names[0], "\n")
		slf.Host = names[0]
	}

	return
}
<commit_msg>send timeout<commit_after>// Package directEmail support direct send email from selected interface include SOCKS5 proxy server
package directEmail

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"golang.org/x/net/proxy"
	"net"
	"net/smtp"
	"net/url"
	"strings"
	"time"
)

// A Email contains options for send email.
type Email struct {
	// Ip contains local IP address which use for send email
	// if blank use default interface
	// if use socks5 proxy "socks://123.124.125.126:8080"
	// and with auth "socks://user:password@123.124.125.126:8080"
	Ip string
	// Host is host name
	// if blank use DNS resolv for field fill
	Host string
	// Port SMTP server port
	Port uint16
	// MapIp use for translate local IP to global if NAT
	// if use Socks server translate IP SOCKS server to real IP
	MapIp map[string]string
	// FromEmail sender email (Required)
	FromEmail string
	// FromName sender name
	FromName string
	// ToEmail recipient email (Required)
	ToEmail string
	// ToName recipient name
	ToName string
	// Subject email subject
	Subject string

	headers     []string
	textPlain   []byte
	textHTML    []byte
	attachments [][]byte
	raw         bytes.Buffer
	bodyLenght  int
}

const (
	debugIs     = true
	connTimeout = 5 * time.Minute // SMTP RFC
)

// New returns a new Email instance for create and send email
func New() Email {
	return Email{Port: 25}
}

// SendThroughServer send email from SMTP server
func (slf *Email) SendThroughServer(host string, port uint16, username, password string) error {
	slf.Port = port

	dialFunc, err := slf.dialFunction()
	debug("Dialer selected, now dial to server\n")

	conn, err := dialFunc("tcp", net.JoinHostPort(host, fmt.Sprintf("%d", slf.Port)))
	if err != nil {
		debug("Not connected\n")
		return err
	}
	defer conn.Close()
	debug("Connected\n")

	err = conn.SetDeadline(time.Now().Add(connTimeout + time.Millisecond*10))
	if err != nil {
		return err
	}

	client, err := smtp.NewClient(conn, host)
	if err != nil {
		return err
	}

	auth := smtp.PlainAuth(
		"",
		username,
		password,
		host,
	)
	slf.Host = "localhost"

	debug("Connected, now send email\n")

	slf.cleanEmail()
	return slf.sendWithTimeout(auth, host, client)
}

// Send email directly
func (slf *Email) Send() error {
	var err error

	server, err := slf.domainFromEmail(slf.ToEmail)
	if err != nil {
		return errors.New("553 Bad ToEmail")
	}

	dialFunc, err := slf.dialFunction()
	if err != nil {
		return fmt.Errorf("421 %v", err)
	}

	conn, err := slf.newClient(server, dialFunc)
	if err != nil {
		debug("Not connected\n")
		return fmt.Errorf("421 %v", err)
	}
	defer conn.Close()
	debug("Connected\n")

	slf.cleanEmail()
	return slf.sendWithTimeout(nil, "", conn)
}

func (slf *Email) cleanEmail() {
	slf.ToEmail = strings.TrimSpace(slf.ToEmail)
	slf.FromEmail = strings.TrimSpace(slf.FromEmail)
}

var testHookStartTLS func(*tls.Config)

// sendWithTimeout hack for bad server
func (slf *Email) sendWithTimeout(auth smtp.Auth, host string, client *smtp.Client) error {
	var res error
	err := make(chan error, 1)
	go func() {
		err <- slf.send(auth, host, client)
	}()

	select {
	case res = <-err:

	case <-time.After(connTimeout):
		res = fmt.Errorf("421 send timeout after %s", connTimeout.String())
	}

	return res
}

// send sending email message
func (slf *Email) send(auth smtp.Auth, host string, client *smtp.Client) error {
	var err error

	if err := client.Hello(strings.TrimRight(slf.Host, ".")); err != nil {
		return err
	}

	if auth != nil {
		if ok, _ := client.Extension("STARTTLS"); ok {
			config := &tls.Config{ServerName: host}
			if testHookStartTLS != nil {
				testHookStartTLS(config)
			}
			if err = client.StartTLS(config); err != nil {
				return err
			}
		}
		if auth != nil {
			if err = client.Auth(auth); err != nil {
				return err
			}
		}
	}

	if err := client.Mail(slf.FromEmail); err != nil {
		return err
	}

	if err := client.Rcpt(slf.ToEmail); err != nil {
		return err
	}

	w, err := client.Data()
	if err != nil {
		return err
	}

	_, err = fmt.Fprint(w, slf.raw.String())
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	return client.Quit()

}

type conn func(network, address string) (net.Conn, error)

func (slf *Email) dialFunction() (conn, error) {
	var dialFunc conn

	if slf.Ip == "" {
		iface := net.Dialer{}
		dialFunc = iface.Dial
		debug("Dial function is default network interface\n")
	} else {
		if strings.ToLower(slf.Ip[0:8]) == "socks://" {
			u, err := url.Parse(slf.Ip)
			if err != nil {
				return nil, fmt.Errorf("Error parse socks: %s", err.Error())
			}
			var iface proxy.Dialer
			if u.User != nil {
				auth := proxy.Auth{}
				auth.User = u.User.Username()
				auth.Password, _ = u.User.Password()
				iface, err = proxy.SOCKS5("tcp", u.Host, &auth, proxy.FromEnvironment())
				if err != nil {
					return dialFunc, err
				}
			} else {
				iface, err = proxy.SOCKS5("tcp", u.Host, nil, proxy.FromEnvironment())
				if err != nil {
					return dialFunc, err
				}
			}
			slf.Ip = u.Host
			dialFunc = iface.Dial
			debug("Dial function is socks proxy from ", slf.Ip[8:], "\n")
		} else {
			addr := &net.TCPAddr{
				IP: net.ParseIP(slf.Ip),
			}
			iface := net.Dialer{
				LocalAddr: addr,
			}
			dialFunc = iface.Dial
			debug("Dial function is ", addr.String(), " network interface\n")
		}
	}

	return dialFunc, nil
}

func (slf *Email) newClient(server string, dialFunc conn) (client *smtp.Client, err error) {
	var conn net.Conn

	records, err := net.LookupMX(server)
	if err != nil {
		return
	}
	debug("MX for domain:\n")
	for i := range records {
		debug(" - ", records[i].Pref, " ", records[i].Host, "\n")
	}

	for i := range records {
		server := strings.TrimRight(strings.TrimSpace(records[i].Host), ".")
		debug("Connect to server ", server, "\n")
		conn, err = dialFunc("tcp", net.JoinHostPort(server, fmt.Sprintf("%d", slf.Port)))
		if err != nil {
			debug("Not connected\n")
			continue
		}
		debug("Connected\n")
		client, err = smtp.NewClient(conn, server)
		if err == nil {
			break
		}
	}
	if err != nil {
		return
	}

	deadline := time.Now().Add(connTimeout + time.Millisecond*10)
	err = conn.SetDeadline(deadline)
	if err != nil {
		return
	}

	if slf.Ip == "" {
		slf.Ip = conn.LocalAddr().String()
	}

	if slf.Host == "" {
		var myGlobalIP string
		myIP, _, err := net.SplitHostPort(strings.TrimLeft(slf.Ip, "socks://"))
		myGlobalIP, ok := slf.MapIp[myIP]
		if !ok {
			myGlobalIP = myIP
		}
		names, err := net.LookupAddr(myGlobalIP)
		if err != nil && len(names) < 1 {
			return nil, err
		}
		debug("LookUp ", myGlobalIP, " this result ", names[0], "\n")
		slf.Host = names[0]
	}

	return
}
<|endoftext|>


<commit_before>package raspicamera

import (
	"github.com/TIBCOSoftware/flogo-lib/core/activity"
	"github.com/TIBCOSoftware/flogo-lib/logger"
	"github.com/dhowden/raspicam"
	"time"
	"os"
	//"path"
)

// log is the default package logger
var log = logger.GetLogger("activity-raspicamera")

const (
	ivTimeout 		= "timeout" //delay before the image is taken
	ivSharpness 	= "sharpness"
	ivBrightness 	= "brightness"
	ivContrast		= "contrast"
	ivSaturation 	= "saturation"
	ivISO			= "iso"
	ivFilename		= "filename"

	ovStatus        = "status"
)

// RaspicameraActivity is a stub for your Activity implementation
type RaspicameraActivity struct {
	metadata *activity.Metadata
}

// NewActivity creates a new activity
func NewActivity(metadata *activity.Metadata) activity.Activity {
	return &RaspicameraActivity{metadata: metadata}
}

// Metadata implements activity.Activity.Metadata
func (a *RaspicameraActivity) Metadata() *activity.Metadata {
	return a.metadata
}

// Eval implements activity.Activity.Eval
func (a *RaspicameraActivity) Eval(context activity.Context) (done bool, err error) {
	timeout := context.GetInput(ivTimeout)
	sharpness := context.GetInput(ivSharpness)
	brightness := context.GetInput(ivBrightness)
	contrast := context.GetInput(ivContrast)
	saturation := context.GetInput(ivSaturation)
	iso := context.GetInput(ivISO)
	filename := context.GetInput(ivFilename)

	// Check if mandatory credentials are set in config
	if filename == nil {
		log.Error("Missing output filename")
		err := activity.NewError("Raspicam filename config not specified", "", nil)
		return false, err
	}

	// Create a client for raspicam.
	still := raspicam.NewStill()
	//preview := still.Preview
	//still.Preview = preview
	const params = "--nopreview"
	still.Command = params

	//preview := raspicam.Preview { Mode: raspicam.PreviewDisabled }

	if timeout != nil {
		still.Timeout = time.Duration(timeout.(int))
		log.Debug("Camera timeout set to %v", timeout)
	}
	if sharpness != nil {
		still.Camera.Sharpness = sharpness.(int)
		log.Debug("Camera sharpness set to %v", sharpness)
	}
	if brightness != nil {
		still.Camera.Brightness = brightness.(int)
		log.Debug("Camera brightness set to %v", brightness)
	}
	if contrast != nil {
		still.Camera.Contrast = contrast.(int)
		log.Debug("Camera contrast set to %v", contrast)
	}
	if saturation != nil {
		still.Camera.Saturation = saturation.(int)
		log.Debug("Camera saturation set to %v", saturation)
	}
	if iso != nil {
		still.Camera.ISO = iso.(int)
		log.Debug("Camera iso set to %v", iso)
	}

	/*
	imageDirectory, imageFile := path.Split(filename.(string))
	if imageFile == "" {
		context.SetOutput(ovStatus, "NO_FILENAME_ERR")
		return true, nil
	}
	if imageDirectory == "" {
		if _, err := os.Stat(imageDirectory); os.IsNotExist(err) {
			os.MkdirAll(imageDirectory, 0777)
		}
	}*/

	// create the folder for the image
	f, err := os.Create(filename.(string))
	if err != nil {
		log.Error("Raspicam error on creating the image file: %v", err)
		context.SetOutput(ovStatus, "IMAGE_CREATE__ERR")
		return true, nil
		//fmt.Fprintf(os.Stderr, "create file: %v", err)

	}
	defer f.Close()

	errCh := make(chan error)
	go func() {
		for x := range errCh {
			//fmt.Fprintf(os.Stderr, "%v\n", x)
			log.Error("%v\n", x)
		}
	}()

	//cmd := exec.Command("raspistill", "-vf", "-hf", "-a", "1024", "-a", "8", "-a", "achimera| %F %r", "-o", imageFile)
	raspicam.Capture(still, f, errCh)
	log.Info("Raspicam created image file: %v", filename)

	context.SetOutput(ovStatus, "OK")

	return true, nil
}
<commit_msg>preview<commit_after>package raspicamera

import (
	"github.com/TIBCOSoftware/flogo-lib/core/activity"
	"github.com/TIBCOSoftware/flogo-lib/logger"
	"github.com/dhowden/raspicam"
	"time"
	"os"
	//"path"
)

// log is the default package logger
var log = logger.GetLogger("activity-raspicamera")

const (
	ivTimeout 		= "timeout" //delay before the image is taken
	ivSharpness 	= "sharpness"
	ivBrightness 	= "brightness"
	ivContrast		= "contrast"
	ivSaturation 	= "saturation"
	ivISO			= "iso"
	ivFilename		= "filename"

	ovStatus        = "status"
)

// RaspicameraActivity is a stub for your Activity implementation
type RaspicameraActivity struct {
	metadata *activity.Metadata
}

// NewActivity creates a new activity
func NewActivity(metadata *activity.Metadata) activity.Activity {
	return &RaspicameraActivity{metadata: metadata}
}

// Metadata implements activity.Activity.Metadata
func (a *RaspicameraActivity) Metadata() *activity.Metadata {
	return a.metadata
}

// Eval implements activity.Activity.Eval
func (a *RaspicameraActivity) Eval(context activity.Context) (done bool, err error) {
	timeout := context.GetInput(ivTimeout)
	sharpness := context.GetInput(ivSharpness)
	brightness := context.GetInput(ivBrightness)
	contrast := context.GetInput(ivContrast)
	saturation := context.GetInput(ivSaturation)
	iso := context.GetInput(ivISO)
	filename := context.GetInput(ivFilename)

	// Check if mandatory credentials are set in config
	if filename == nil {
		log.Error("Missing output filename")
		err := activity.NewError("Raspicam filename config not specified", "", nil)
		return false, err
	}

	// Create a client for raspicam.
	still := raspicam.NewStill()
	//preview := still.Preview
	//still.Preview = preview

	mode := raspicam.PreviewDisabled
	still.Preview.Mode = mode

	//preview := raspicam.Preview { Mode: raspicam.PreviewDisabled }

	if timeout != nil {
		still.Timeout = time.Duration(timeout.(int))
		log.Debug("Camera timeout set to %v", timeout)
	}
	if sharpness != nil {
		still.Camera.Sharpness = sharpness.(int)
		log.Debug("Camera sharpness set to %v", sharpness)
	}
	if brightness != nil {
		still.Camera.Brightness = brightness.(int)
		log.Debug("Camera brightness set to %v", brightness)
	}
	if contrast != nil {
		still.Camera.Contrast = contrast.(int)
		log.Debug("Camera contrast set to %v", contrast)
	}
	if saturation != nil {
		still.Camera.Saturation = saturation.(int)
		log.Debug("Camera saturation set to %v", saturation)
	}
	if iso != nil {
		still.Camera.ISO = iso.(int)
		log.Debug("Camera iso set to %v", iso)
	}

	/*
	imageDirectory, imageFile := path.Split(filename.(string))
	if imageFile == "" {
		context.SetOutput(ovStatus, "NO_FILENAME_ERR")
		return true, nil
	}
	if imageDirectory == "" {
		if _, err := os.Stat(imageDirectory); os.IsNotExist(err) {
			os.MkdirAll(imageDirectory, 0777)
		}
	}*/

	// create the folder for the image
	f, err := os.Create(filename.(string))
	if err != nil {
		log.Error("Raspicam error on creating the image file: ", err)
		context.SetOutput(ovStatus, "IMAGE_CREATE__ERR")
		return true, nil
		//fmt.Fprintf(os.Stderr, "create file: %v", err)

	}
	defer f.Close()

	errCh := make(chan error)
	go func() {
		for x := range errCh {
			//fmt.Fprintf(os.Stderr, "%v\n", x)
			log.Error("Error %v\n", x)
		}
	}()

	//cmd := exec.Command("raspistill", "-vf", "-hf", "-a", "1024", "-a", "8", "-a", "achimera| %F %r", "-o", imageFile)
	raspicam.Capture(still, f, errCh)
	log.Info("Raspicam created image file: ", filename)

	context.SetOutput(ovStatus, "OK")

	return true, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"errors"
	"io"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/moby/moby/pkg/term"
	context "golang.org/x/net/context"

	pb "github.com/kata-containers/agent/protocols/grpc"
)

const sigChanSize = 2048

var sigIgnored = map[syscall.Signal]bool{
	syscall.SIGCHLD:  true,
	syscall.SIGPIPE:  true,
	syscall.SIGWINCH: true,
}

type shim struct {
	containerID string
	execID      string

	ctx   context.Context
	agent *shimAgent
}

func newShim(addr, containerID, execID string) (*shim, error) {
	agent, err := newShimAgent(addr)
	if err != nil {
		return nil, err
	}

	return &shim{containerID: containerID,
		execID: execID,
		ctx:    context.Background(),
		agent:  agent}, nil
}

func (s *shim) proxyStdio(wg *sync.WaitGroup, terminal bool) {
	// don't wait the copying of the stdin, because `io.Copy(inPipe, os.Stdin)`
	// can't terminate when no input. todo: find a better way.
	wg.Add(1)
	if !terminal {
		// In case it's not a terminal, we also need to get the output
		// from stderr.
		wg.Add(1)
	}

	inPipe, outPipe, errPipe := shimStdioPipe(s.ctx, s.agent, s.containerID, s.execID)
	go func() {
		_, err1 := io.Copy(inPipe, os.Stdin)
		_, err2 := s.agent.CloseStdin(s.ctx, &pb.CloseStdinRequest{
			ContainerId: s.containerID,
			ExecId:      s.execID})
		if err1 != nil {
			logger().WithError(err1).Warn("copy stdin failed")
		}
		if err2 != nil {
			logger().WithError(err2).Warn("close stdin failed")
		}
	}()

	go func() {
		_, err := io.Copy(os.Stdout, outPipe)
		logger().WithError(err).Info("copy stdout failed")
		wg.Done()
	}()

	if !terminal {
		go func() {
			_, err := io.Copy(os.Stderr, errPipe)
			logger().WithError(err).Info("copy stderr failed")
			wg.Done()
		}()
	}
}

func (s *shim) forwardAllSignals() chan os.Signal {
	sigc := make(chan os.Signal, sigChanSize)
	// handle all signals for the process.
	signal.Notify(sigc)
	signal.Ignore(syscall.SIGCHLD, syscall.SIGPIPE)

	go func() {
		for sig := range sigc {
			sysSig, ok := sig.(syscall.Signal)
			if !ok {
				err := errors.New("unknown signal")
				logger().WithError(err).WithField("signal", sig.String()).Error("")
				continue
			}

			if fatalSignal(sysSig) {
				logger().WithField("signal", sig).Error("received fatal signal")
				die()
			}

			if sigIgnored[sysSig] {
				//ignore these
				continue
			}
			// forward this signal to container
			_, err := s.agent.SignalProcess(s.ctx, &pb.SignalProcessRequest{
				ContainerId: s.containerID,
				ExecId:      s.execID,
				Signal:      uint32(sysSig)})
			if err != nil {
				logger().WithError(err).WithField("signal", sig.String()).Error("forward signal failed")
			}
		}
	}()
	return sigc
}

func (s *shim) resizeTty(fromTty *os.File) error {
	ws, err := term.GetWinsize(fromTty.Fd())
	if err != nil {
		logger().WithError(err).Info("Error getting size")
		return nil
	}

	_, err = s.agent.TtyWinResize(s.ctx, &pb.TtyWinResizeRequest{
		ContainerId: s.containerID,
		ExecId:      s.execID,
		Row:         uint32(ws.Height),
		Column:      uint32(ws.Width)})
	if err != nil {
		logger().WithError(err).Error("set winsize failed")
	}

	return err
}

func (s *shim) monitorTtySize(tty *os.File) {
	s.resizeTty(tty)
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGWINCH)
	go func() {
		for range sigchan {
			s.resizeTty(tty)
		}
	}()
}

func (s *shim) wait() (int32, error) {
	resp, err := s.agent.WaitProcess(s.ctx, &pb.WaitProcessRequest{
		ContainerId: s.containerID,
		ExecId:      s.execID})
	if err != nil {
		return 0, err
	}

	return resp.Status, nil
}
<commit_msg>logging: Only log actual I/O errors<commit_after>// Copyright 2017 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"errors"
	"io"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/moby/moby/pkg/term"
	context "golang.org/x/net/context"

	pb "github.com/kata-containers/agent/protocols/grpc"
)

const sigChanSize = 2048

var sigIgnored = map[syscall.Signal]bool{
	syscall.SIGCHLD:  true,
	syscall.SIGPIPE:  true,
	syscall.SIGWINCH: true,
}

type shim struct {
	containerID string
	execID      string

	ctx   context.Context
	agent *shimAgent
}

func newShim(addr, containerID, execID string) (*shim, error) {
	agent, err := newShimAgent(addr)
	if err != nil {
		return nil, err
	}

	return &shim{containerID: containerID,
		execID: execID,
		ctx:    context.Background(),
		agent:  agent}, nil
}

func (s *shim) proxyStdio(wg *sync.WaitGroup, terminal bool) {
	// don't wait the copying of the stdin, because `io.Copy(inPipe, os.Stdin)`
	// can't terminate when no input. todo: find a better way.
	wg.Add(1)
	if !terminal {
		// In case it's not a terminal, we also need to get the output
		// from stderr.
		wg.Add(1)
	}

	inPipe, outPipe, errPipe := shimStdioPipe(s.ctx, s.agent, s.containerID, s.execID)
	go func() {
		_, err1 := io.Copy(inPipe, os.Stdin)
		_, err2 := s.agent.CloseStdin(s.ctx, &pb.CloseStdinRequest{
			ContainerId: s.containerID,
			ExecId:      s.execID})
		if err1 != nil {
			logger().WithError(err1).Warn("copy stdin failed")
		}
		if err2 != nil {
			logger().WithError(err2).Warn("close stdin failed")
		}
	}()

	go func() {
		_, err := io.Copy(os.Stdout, outPipe)
		if err != nil {
			logger().WithError(err).Info("copy stdout failed")
		}

		wg.Done()
	}()

	if !terminal {
		go func() {
			_, err := io.Copy(os.Stderr, errPipe)
			if err != nil {
				logger().WithError(err).Info("copy stderr failed")
			}

			wg.Done()
		}()
	}
}

func (s *shim) forwardAllSignals() chan os.Signal {
	sigc := make(chan os.Signal, sigChanSize)
	// handle all signals for the process.
	signal.Notify(sigc)
	signal.Ignore(syscall.SIGCHLD, syscall.SIGPIPE)

	go func() {
		for sig := range sigc {
			sysSig, ok := sig.(syscall.Signal)
			if !ok {
				err := errors.New("unknown signal")
				logger().WithError(err).WithField("signal", sig.String()).Error("")
				continue
			}

			if fatalSignal(sysSig) {
				logger().WithField("signal", sig).Error("received fatal signal")
				die()
			}

			if sigIgnored[sysSig] {
				//ignore these
				continue
			}
			// forward this signal to container
			_, err := s.agent.SignalProcess(s.ctx, &pb.SignalProcessRequest{
				ContainerId: s.containerID,
				ExecId:      s.execID,
				Signal:      uint32(sysSig)})
			if err != nil {
				logger().WithError(err).WithField("signal", sig.String()).Error("forward signal failed")
			}
		}
	}()
	return sigc
}

func (s *shim) resizeTty(fromTty *os.File) error {
	ws, err := term.GetWinsize(fromTty.Fd())
	if err != nil {
		logger().WithError(err).Info("Error getting size")
		return nil
	}

	_, err = s.agent.TtyWinResize(s.ctx, &pb.TtyWinResizeRequest{
		ContainerId: s.containerID,
		ExecId:      s.execID,
		Row:         uint32(ws.Height),
		Column:      uint32(ws.Width)})
	if err != nil {
		logger().WithError(err).Error("set winsize failed")
	}

	return err
}

func (s *shim) monitorTtySize(tty *os.File) {
	s.resizeTty(tty)
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGWINCH)
	go func() {
		for range sigchan {
			s.resizeTty(tty)
		}
	}()
}

func (s *shim) wait() (int32, error) {
	resp, err := s.agent.WaitProcess(s.ctx, &pb.WaitProcessRequest{
		ContainerId: s.containerID,
		ExecId:      s.execID})
	if err != nil {
		return 0, err
	}

	return resp.Status, nil
}
<|endoftext|>


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

package nvim

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/neovim-go/vim"
)

const pkgBuffer = "nvim.buffer"

// Buf represents a Neovim buffer.
// TODO(zchee): Ugly type name because of a conflict in vim.Buffer.
type Buf struct {
	v *vim.Vim
	p *vim.Pipeline

	vim.Buffer
	Name     string
	Filetype string
	Bufnr    int
	Mode     string
	Data     []byte

	WindowContext
	TabpageContext
}

// NewBuffer return the new Buf instance with goroutine pipeline.
func NewBuffer(v *vim.Vim) *Buf {
	return &Buf{
		v: v,
		p: v.NewPipeline(),
	}
}

// Create creates the new buffer and return the Buffer structure type.
func (b *Buf) Create(name, filetype, mode string, option map[NvimOption]map[string]interface{}) error {
	b.Name = name
	b.Filetype = filetype
	b.Mode = mode

	err := b.v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	b.p.CurrentBuffer(&b.Buffer)
	b.p.CurrentWindow(&b.Window)
	b.p.CurrentTabpage(&b.Tabpage)
	if err := b.p.Wait(); err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	b.p.BufferNumber(b.Buffer, &b.Bufnr)

	if option != nil {
		if option[BufferOption] != nil {
			for k, op := range option[BufferOption] {
				b.p.SetBufferOption(b.Buffer, k, op)
			}
		}
		if option[BufferVar] != nil {
			for k, op := range option[BufferVar] {
				b.p.SetBufferVar(b.Buffer, k, op, nil)
			}
		}
		if option[WindowOption] != nil {
			for k, op := range option[WindowOption] {
				b.p.SetWindowOption(b.Window, k, op)
			}
		}
		if option[WindowVar] != nil {
			for k, op := range option[WindowVar] {
				b.p.SetWindowVar(b.Window, k, op, nil)
			}
		}
		if option[TabpageVar] != nil {
			for k, op := range option[TabpageVar] {
				b.p.SetTabpageVar(b.Tabpage, k, op, nil)
			}
		}
	}

	if !strings.Contains(b.Name, ".") {
		b.p.Command(fmt.Sprintf("runtime! syntax/%s.vim", filetype))
	}

	return b.p.Wait()
}

func (b *Buf) GetBufferContext() {
	b.p.CurrentBuffer(&b.Buffer)
	b.p.CurrentWindow(&b.Window)
	b.p.CurrentTabpage(&b.Tabpage)

	b.p.Wait()
}

func (b *Buf) BufferLines(start, end int, strict bool) {
	if b.Buffer == 0 {
		b.GetBufferContext()
	}

	buf, err := b.v.BufferLines(b.Buffer, start, end, strict)
	if err != nil {
		return
	}
	b.Data = ToByteSlice(buf)
}

func (b *Buf) SetBufferLines(start, end int, strict bool, replacement []byte) error {
	if b.Buffer == 0 {
		err := errors.New("Does not exist of target buffer")
		return err
	}

	b.Data = replacement

	return b.v.SetBufferLines(b.Buffer, start, end, strict, ToBufferLines(replacement))
}

func (b *Buf) SetBufferLinesAll(replacement []byte) error {
	if b.Buffer == 0 {
		err := errors.New("Does not exist of target buffer")
		return err
	}

	b.Data = replacement
	b.Write(b.Data)

	return nil
}

// UpdateSyntax updates the syntax highlight of the buffer.
func (b *Buf) UpdateSyntax(syntax string) {
	if b.Name != "" {
		b.v.SetBufferName(b.Buffer, b.Name)
	}

	if syntax == "" {
		var filetype interface{}
		b.v.BufferOption(b.Buffer, "filetype", &filetype)
		syntax = fmt.Sprintf("%s", filetype)
	}

	b.v.Command(fmt.Sprintf("runtime! syntax/%s.vim", syntax))
}

// SetLocalMapping sets buffer local mapping.
// 'mapping' arg: [key]{destination}
func (b *Buf) SetLocalMapping(mode string, mapping map[string]string) error {
	if mapping != nil {
		cwin, err := b.v.CurrentWindow()
		if err != nil {
			return errors.Annotate(err, "nvim/buffer.SetMapping")
		}

		b.p.SetCurrentWindow(b.Window)
		defer b.v.SetCurrentWindow(cwin)

		for k, v := range mapping {
			b.p.Command(fmt.Sprintf("silent %s <buffer><silent>%s %s", mode, k, v))
		}
	}

	return b.p.Wait()
}

// lineCount counts the Neovim buffer line count and check whether 1 count,
// Because new(empty) buffer and one line buffer are both 1 count.
func (b *Buf) lineCount() (int, error) {
	lineCount, err := b.v.BufferLineCount(b.Buffer)
	if err != nil {
		return 0, errors.Annotate(err, pkgBuffer)
	}

	if lineCount == 1 {
		line, err := b.v.CurrentLine()
		if err != nil {
			return 0, errors.Annotate(err, pkgBuffer)
		}
		// Set 0 to lineCount if buffer is empty
		if len(line) == 0 {
			lineCount = 0
		}
	}

	return lineCount, nil
}

// Write appends the contents of p to the Neovim buffer.
func (b *Buf) Write(p []byte) (int, error) {
	lineCount, err := b.lineCount()
	if err != nil {
		return 0, errors.Annotate(err, pkgBuffer)
	}

	buf := bytes.NewBuffer(p)
	b.v.SetBufferLines(b.Buffer, lineCount, -1, true, ToBufferLines(buf.Bytes()))

	return len(p), nil
}

// WriteString appends the contents of s to the Neovim buffer.
func (b *Buf) WriteString(s string) error {
	lineCount, err := b.lineCount()
	if err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	buf := bytes.NewBufferString(s)

	return b.v.SetBufferLines(b.Buffer, lineCount, -1, true, ToBufferLines(buf.Bytes()))
}

// Truncate discards all but the first n unread bytes from the
// Neovim buffer but continues to use the same allocated storage.
func (b *Buf) Truncate(n int) {
	b.v.SetBufferLines(b.Buffer, n, -1, true, [][]byte{})
}

// Reset resets the Neovim buffer to be empty,
// but it retains the underlying storage for use by future writes.
// Reset is the same as Truncate(0).
func (b *Buf) Reset() { b.Truncate(0) }

// ----------------------------------------------------------------------------
// Utility

// IsBufferValid wrapper of v.IsBufferValid function.
func IsBufferValid(v *vim.Vim, b vim.Buffer) bool {
	res, err := v.IsBufferValid(b)
	if err != nil {
		return false
	}
	return res
}

// BufContains reports whether buffer list is within b.
func IsBufContains(v *vim.Vim, b vim.Buffer) bool {
	bufs, _ := v.Buffers()
	for _, buf := range bufs {
		if buf == b {
			return true
		}
	}
	return false
}

// BufExists reports whether buffer list is within bufnr use vim bufexists function.
func IsBufExists(v *vim.Vim, bufnr int) bool {
	var res interface{}
	v.Call("bufexists", &res, bufnr)

	return res.(int64) != 0
}

// Modifiable sets modifiable to true,
// The returned function restores modifiable to false.
func Modifiable(v *vim.Vim, b vim.Buffer) func() {
	v.SetBufferOption(b, BufOptionModifiable, true)

	return func() {
		v.SetBufferOption(b, BufOptionModifiable, false)
	}
}

// ToByteSlice converts the 2D buffer byte data to sigle byte slice.
func ToByteSlice(byt [][]byte) []byte { return bytes.Join(byt, []byte{'\n'}) }

// ToBufferLines converts the byte slice to the 2D byte slice of Neovim buffer data.
func ToBufferLines(byt []byte) [][]byte { return bytes.Split(byt, []byte{'\n'}) }

// ByteOffset calculates the byte-offset of current cursor position.
func ByteOffset(v *vim.Vim, b vim.Buffer, w vim.Window) (int, error) {
	cursor, _ := v.WindowCursor(w)
	byteBuf, _ := v.BufferLines(b, 0, -1, true)

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	var offset int
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

// ByteOffsetPipe calculates the byte-offset of current cursor position uses vim.Pipeline.
func ByteOffsetPipe(p *vim.Pipeline, b vim.Buffer, w vim.Window) (int, error) {
	var cursor [2]int
	p.WindowCursor(w, &cursor)

	var byteBuf [][]byte
	p.BufferLines(b, 0, -1, true, &byteBuf)

	if err := p.Wait(); err != nil {
		return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
	}

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}
<commit_msg>nvim/buffer: Use GetBufferContext() and Add error handle<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvim

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/neovim-go/vim"
)

const pkgBuffer = "nvim.buffer"

// Buf represents a Neovim buffer.
// TODO(zchee): Ugly type name because of a conflict in vim.Buffer.
type Buf struct {
	v *vim.Vim
	p *vim.Pipeline

	vim.Buffer
	Name     string
	Filetype string
	Bufnr    int
	Mode     string
	Data     []byte

	WindowContext
	TabpageContext
}

// NewBuffer return the new Buf instance with goroutine pipeline.
func NewBuffer(v *vim.Vim) *Buf {
	return &Buf{
		v: v,
		p: v.NewPipeline(),
	}
}

// Create creates the new buffer and return the Buffer structure type.
func (b *Buf) Create(name, filetype, mode string, option map[NvimOption]map[string]interface{}) error {
	b.Name = name
	b.Filetype = filetype
	b.Mode = mode

	err := b.v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	if err := b.GetBufferContext(); err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	b.p.BufferNumber(b.Buffer, &b.Bufnr)

	if option != nil {
		if option[BufferOption] != nil {
			for k, op := range option[BufferOption] {
				b.p.SetBufferOption(b.Buffer, k, op)
			}
		}
		if option[BufferVar] != nil {
			for k, op := range option[BufferVar] {
				b.p.SetBufferVar(b.Buffer, k, op, nil)
			}
		}
		if option[WindowOption] != nil {
			for k, op := range option[WindowOption] {
				b.p.SetWindowOption(b.Window, k, op)
			}
		}
		if option[WindowVar] != nil {
			for k, op := range option[WindowVar] {
				b.p.SetWindowVar(b.Window, k, op, nil)
			}
		}
		if option[TabpageVar] != nil {
			for k, op := range option[TabpageVar] {
				b.p.SetTabpageVar(b.Tabpage, k, op, nil)
			}
		}
	}

	if !strings.Contains(b.Name, ".") {
		b.p.Command(fmt.Sprintf("runtime! syntax/%s.vim", filetype))
	}

	return b.p.Wait()
}

func (b *Buf) GetBufferContext() error {
	b.p.CurrentBuffer(&b.Buffer)
	b.p.CurrentWindow(&b.Window)
	b.p.CurrentTabpage(&b.Tabpage)

	return b.p.Wait()
}

func (b *Buf) BufferLines(start, end int, strict bool) {
	if b.Buffer == 0 {
		b.GetBufferContext()
	}

	buf, err := b.v.BufferLines(b.Buffer, start, end, strict)
	if err != nil {
		return
	}
	b.Data = ToByteSlice(buf)
}

func (b *Buf) SetBufferLines(start, end int, strict bool, replacement []byte) error {
	if b.Buffer == 0 {
		err := errors.New("Does not exist of target buffer")
		return err
	}

	b.Data = replacement

	return b.v.SetBufferLines(b.Buffer, start, end, strict, ToBufferLines(replacement))
}

func (b *Buf) SetBufferLinesAll(replacement []byte) error {
	if b.Buffer == 0 {
		err := errors.New("Does not exist of target buffer")
		return err
	}

	b.Data = replacement
	b.Write(b.Data)

	return nil
}

// UpdateSyntax updates the syntax highlight of the buffer.
func (b *Buf) UpdateSyntax(syntax string) {
	if b.Name != "" {
		b.v.SetBufferName(b.Buffer, b.Name)
	}

	if syntax == "" {
		var filetype interface{}
		b.v.BufferOption(b.Buffer, "filetype", &filetype)
		syntax = fmt.Sprintf("%s", filetype)
	}

	b.v.Command(fmt.Sprintf("runtime! syntax/%s.vim", syntax))
}

// SetLocalMapping sets buffer local mapping.
// 'mapping' arg: [key]{destination}
func (b *Buf) SetLocalMapping(mode string, mapping map[string]string) error {
	if mapping != nil {
		cwin, err := b.v.CurrentWindow()
		if err != nil {
			return errors.Annotate(err, "nvim/buffer.SetMapping")
		}

		b.p.SetCurrentWindow(b.Window)
		defer b.v.SetCurrentWindow(cwin)

		for k, v := range mapping {
			b.p.Command(fmt.Sprintf("silent %s <buffer><silent>%s %s", mode, k, v))
		}
	}

	return b.p.Wait()
}

// lineCount counts the Neovim buffer line count and check whether 1 count,
// Because new(empty) buffer and one line buffer are both 1 count.
func (b *Buf) lineCount() (int, error) {
	lineCount, err := b.v.BufferLineCount(b.Buffer)
	if err != nil {
		return 0, errors.Annotate(err, pkgBuffer)
	}

	if lineCount == 1 {
		line, err := b.v.CurrentLine()
		if err != nil {
			return 0, errors.Annotate(err, pkgBuffer)
		}
		// Set 0 to lineCount if buffer is empty
		if len(line) == 0 {
			lineCount = 0
		}
	}

	return lineCount, nil
}

// Write appends the contents of p to the Neovim buffer.
func (b *Buf) Write(p []byte) (int, error) {
	lineCount, err := b.lineCount()
	if err != nil {
		return 0, errors.Annotate(err, pkgBuffer)
	}

	buf := bytes.NewBuffer(p)
	b.v.SetBufferLines(b.Buffer, lineCount, -1, true, ToBufferLines(buf.Bytes()))

	return len(p), nil
}

// WriteString appends the contents of s to the Neovim buffer.
func (b *Buf) WriteString(s string) error {
	lineCount, err := b.lineCount()
	if err != nil {
		return errors.Annotate(err, pkgBuffer)
	}

	buf := bytes.NewBufferString(s)

	return b.v.SetBufferLines(b.Buffer, lineCount, -1, true, ToBufferLines(buf.Bytes()))
}

// Truncate discards all but the first n unread bytes from the
// Neovim buffer but continues to use the same allocated storage.
func (b *Buf) Truncate(n int) {
	b.v.SetBufferLines(b.Buffer, n, -1, true, [][]byte{})
}

// Reset resets the Neovim buffer to be empty,
// but it retains the underlying storage for use by future writes.
// Reset is the same as Truncate(0).
func (b *Buf) Reset() { b.Truncate(0) }

// ----------------------------------------------------------------------------
// Utility

// IsBufferValid wrapper of v.IsBufferValid function.
func IsBufferValid(v *vim.Vim, b vim.Buffer) bool {
	res, err := v.IsBufferValid(b)
	if err != nil {
		return false
	}
	return res
}

// BufContains reports whether buffer list is within b.
func IsBufContains(v *vim.Vim, b vim.Buffer) bool {
	bufs, _ := v.Buffers()
	for _, buf := range bufs {
		if buf == b {
			return true
		}
	}
	return false
}

// BufExists reports whether buffer list is within bufnr use vim bufexists function.
func IsBufExists(v *vim.Vim, bufnr int) bool {
	var res interface{}
	v.Call("bufexists", &res, bufnr)

	return res.(int64) != 0
}

// Modifiable sets modifiable to true,
// The returned function restores modifiable to false.
func Modifiable(v *vim.Vim, b vim.Buffer) func() {
	v.SetBufferOption(b, BufOptionModifiable, true)

	return func() {
		v.SetBufferOption(b, BufOptionModifiable, false)
	}
}

// ToByteSlice converts the 2D buffer byte data to sigle byte slice.
func ToByteSlice(byt [][]byte) []byte { return bytes.Join(byt, []byte{'\n'}) }

// ToBufferLines converts the byte slice to the 2D byte slice of Neovim buffer data.
func ToBufferLines(byt []byte) [][]byte { return bytes.Split(byt, []byte{'\n'}) }

// ByteOffset calculates the byte-offset of current cursor position.
func ByteOffset(v *vim.Vim, b vim.Buffer, w vim.Window) (int, error) {
	cursor, _ := v.WindowCursor(w)
	byteBuf, _ := v.BufferLines(b, 0, -1, true)

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	var offset int
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

// ByteOffsetPipe calculates the byte-offset of current cursor position uses vim.Pipeline.
func ByteOffsetPipe(p *vim.Pipeline, b vim.Buffer, w vim.Window) (int, error) {
	var cursor [2]int
	p.WindowCursor(w, &cursor)

	var byteBuf [][]byte
	p.BufferLines(b, 0, -1, true, &byteBuf)

	if err := p.Wait(); err != nil {
		return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
	}

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}
<|endoftext|>


<commit_before>package clui

import (
	term "github.com/nsf/termbox-go"
	"sync"
	"sync/atomic"
)

// BaseControl is a base for all visible controls.
// Every new control must inherit it or implement
// the same set of methods
type BaseControl struct {
	refID         int64
	title         string
	x, y          int
	width, height int
	minW, minH    int
	scale         int
	fg, bg        term.Attribute
	fgActive      term.Attribute
	bgActive      term.Attribute
	tabSkip       bool
	disabled      bool
	hidden        bool
	align         Align
	parent        Control
	inactive      bool
	modal         bool
	padX, padY    int
	gapX, gapY    int
	pack          PackType
	children      []Control
	mtx           sync.RWMutex
}

var (
	globalRefId int64
)

func nextRefId() int64 {
	return atomic.AddInt64(&globalRefId, 1)
}

func NewBaseControl() BaseControl {
	return BaseControl{refID: nextRefId()}
}

func (c *BaseControl) RefID() int64 {
	return c.refID
}

func (c *BaseControl) Title() string {
	return c.title
}

func (c *BaseControl) SetTitle(title string) {
	c.title = title
}

func (c *BaseControl) Size() (widht int, height int) {
	return c.width, c.height
}

func (c *BaseControl) SetSize(width, height int) {
	if width < c.minW {
		width = c.minW
	}
	if height < c.minH {
		height = c.minH
	}

	if height != c.height || width != c.width {
		c.height = height
		c.width = width
	}
}

func (c *BaseControl) Pos() (x int, y int) {
	return c.x, c.y
}

func (c *BaseControl) SetPos(x, y int) {
	c.x = x
	c.y = y
}

func (c *BaseControl) applyConstraints() {
	ww, hh := c.width, c.height
	if ww < c.minW {
		ww = c.minW
	}
	if hh < c.minH {
		hh = c.minH
	}
	if hh != c.height || ww != c.width {
		c.SetSize(ww, hh)
	}
}

func (c *BaseControl) Constraints() (minw int, minh int) {
	return c.minW, c.minH
}

func (c *BaseControl) SetConstraints(minw, minh int) {
	c.minW = minw
	c.minH = minh
	c.applyConstraints()
}

func (c *BaseControl) Active() bool {
	return !c.inactive
}

func (c *BaseControl) SetActive(active bool) {
	c.inactive = !active
}

func (c *BaseControl) TabStop() bool {
	return !c.tabSkip
}

func (c *BaseControl) SetTabStop(tabstop bool) {
	c.tabSkip = !tabstop
}

func (c *BaseControl) Enabled() bool {
	c.mtx.RLock()
	defer c.mtx.RUnlock()

	return !c.disabled
}

func (c *BaseControl) SetEnabled(enabled bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	c.disabled = !enabled
}

func (c *BaseControl) Visible() bool {
	c.mtx.RLock()
	defer c.mtx.RUnlock()

	return !c.hidden
}

func (c *BaseControl) SetVisible(visible bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	if visible == !c.hidden {
		return
	}

	c.hidden = !visible
	if c.parent == nil {
		return
	}

	p := c.Parent()
	for p.Parent() != nil {
		p = p.Parent()
	}

	go func() {
		if !c.inactive {
			PutEvent(Event{Type: EventKey, Key: term.KeyTab})
		}
		PutEvent(Event{Type: EventLayout, Target: p})
	}()
}

func (c *BaseControl) Parent() Control {
	return c.parent
}

func (c *BaseControl) SetParent(parent Control) {
	if c.parent == nil {
		c.parent = parent
	}
}

func (c *BaseControl) Modal() bool {
	return c.modal
}

func (c *BaseControl) SetModal(modal bool) {
	c.modal = modal
}

func (c *BaseControl) Paddings() (px int, py int) {
	return c.padX, c.padY
}

func (c *BaseControl) SetPaddings(px, py int) {
	if px >= 0 {
		c.padX = px
	}
	if py >= 0 {
		c.padY = py
	}
}

func (c *BaseControl) Gaps() (dx int, dy int) {
	return c.gapX, c.gapY
}

func (c *BaseControl) SetGaps(dx, dy int) {
	if dx >= 0 {
		c.gapX = dx
	}
	if dy >= 0 {
		c.gapY = dy
	}
}

func (c *BaseControl) Pack() PackType {
	return c.pack
}

func (c *BaseControl) SetPack(pack PackType) {
	c.pack = pack
}

func (c *BaseControl) Scale() int {
	return c.scale
}

func (c *BaseControl) SetScale(scale int) {
	if scale >= 0 {
		c.scale = scale
	}
}

func (c *BaseControl) Align() Align {
	return c.align
}

func (c *BaseControl) SetAlign(align Align) {
	c.align = align
}

func (c *BaseControl) TextColor() term.Attribute {
	return c.fg
}

func (c *BaseControl) SetTextColor(clr term.Attribute) {
	c.fg = clr
}

func (c *BaseControl) BackColor() term.Attribute {
	return c.bg
}

func (c *BaseControl) SetBackColor(clr term.Attribute) {
	c.bg = clr
}

func (c *BaseControl) childCount() int {
	cnt := 0
	for _, child := range c.children {
		if child.Visible() {
			cnt++
		}
	}

	return cnt
}

func (c *BaseControl) ResizeChildren() {
	children := c.childCount()
	if children == 0 {
		return
	}

	fullWidth := c.width - 2*c.padX
	fullHeight := c.height - 2*c.padY
	if c.pack == Horizontal {
		fullWidth -= (children - 1) * c.gapX
	} else {
		fullHeight -= (children - 1) * c.gapY
	}

	totalSc := c.ChildrenScale()
	minWidth := 0
	minHeight := 0
	for _, child := range c.children {
		if !child.Visible() {
			continue
		}

		cw, ch := child.MinimalSize()
		if c.pack == Horizontal {
			minWidth += cw
		} else {
			minHeight += ch
		}
	}

	aStep := 0
	diff := fullWidth - minWidth
	if c.pack == Vertical {
		diff = fullHeight - minHeight
	}
	if totalSc > 0 {
		aStep = int(float32(diff) / float32(totalSc))
	}

	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}

		tw, th := ctrl.MinimalSize()
		sc := ctrl.Scale()
		d := int(ctrl.Scale() * aStep)
		if c.pack == Horizontal {
			if sc != 0 {
				if sc == totalSc {
					tw += diff
					d = diff
				} else {
					tw += d
				}
			}
			th = fullHeight
		} else {
			if sc != 0 {
				if sc == totalSc {
					th += diff
					d = diff
				} else {
					th += d
				}
			}
			tw = fullWidth
		}
		diff -= d
		totalSc -= sc

		ctrl.SetSize(tw, th)
		ctrl.ResizeChildren()
	}
}

func (c *BaseControl) AddChild(control Control) {
	if c.children == nil {
		c.children = make([]Control, 1)
		c.children[0] = control
	} else {
		if c.ChildExists(control) {
			panic("Double adding a child")
		}

		c.children = append(c.children, control)
	}

	var ctrl Control
	var mainCtrl Control
	ctrl = c
	for ctrl != nil {
		ww, hh := ctrl.MinimalSize()
		cw, ch := ctrl.Size()
		if ww > cw || hh > ch {
			if ww > cw {
				cw = ww
			}
			if hh > ch {
				ch = hh
			}
			ctrl.SetConstraints(cw, ch)
		}

		if ctrl.Parent() == nil {
			mainCtrl = ctrl
		}
		ctrl = ctrl.Parent()
	}

	if mainCtrl != nil {
		mainCtrl.ResizeChildren()
		mainCtrl.PlaceChildren()
	}
}

func (c *BaseControl) Children() []Control {
	child := make([]Control, len(c.children))
	copy(child, c.children)
	return child
}

func (c *BaseControl) ChildExists(control Control) bool {
	if len(c.children) == 0 {
		return false
	}

	for _, ctrl := range c.children {
		if ctrl == control {
			return true
		}
	}

	return false
}

func (c *BaseControl) ChildrenScale() int {
	if c.childCount() == 0 {
		return c.scale
	}

	total := 0
	for _, ctrl := range c.children {
		if ctrl.Visible() {
			total += ctrl.Scale()
		}
	}

	return total
}

func (c *BaseControl) MinimalSize() (w int, h int) {
	children := c.childCount()
	if children == 0 {
		return c.minW, c.minH
	}

	totalX := 2 * c.padX
	totalY := 2 * c.padY

	if c.pack == Vertical {
		totalY += (children - 1) * c.gapY
	} else {
		totalX += (children - 1) * c.gapX
	}

	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}
		ww, hh := ctrl.MinimalSize()
		if c.pack == Vertical {
			totalY += hh
			if ww+2*c.padX > totalX {
				totalX = ww + 2*c.padX
			}
		} else {
			totalX += ww
			if hh+2*c.padY > totalY {
				totalY = hh + 2*c.padY
			}
		}
	}

	if totalX < c.minW {
		totalX = c.minW
	}
	if totalY < c.minH {
		totalY = c.minH
	}

	return totalX, totalY
}

func (c *BaseControl) Draw() {
	panic("BaseControl Draw Called")
}

func (c *BaseControl) DrawChildren() {
	if c.hidden {
		return
	}

	PushClip()
	defer PopClip()

	SetClipRect(c.x+c.padX, c.y+c.padY, c.width-2*c.padX, c.height-2*c.padY)

	for _, child := range c.children {
		child.Draw()
	}
}

func (c *BaseControl) HitTest(x, y int) HitResult {
	if x > c.x && x < c.x+c.width-1 &&
		y > c.y && y < c.y+c.height-1 {
		return HitInside
	}

	if (x == c.x || x == c.x+c.width-1) &&
		y >= c.y && y < c.y+c.height {
		return HitBorder
	}

	if (y == c.y || y == c.y+c.height-1) &&
		x >= c.x && x < c.x+c.width {
		return HitBorder
	}

	return HitOutside
}

func (c *BaseControl) ProcessEvent(ev Event) bool {
	return SendEventToChild(c, ev)
}

func (c *BaseControl) PlaceChildren() {
	children := c.childCount()
	if c.children == nil || children == 0 {
		return
	}

	xx, yy := c.x+c.padX, c.y+c.padY
	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}

		ctrl.SetPos(xx, yy)
		ww, hh := ctrl.Size()
		if c.pack == Vertical {
			yy += c.gapY + hh
		} else {
			xx += c.gapX + ww
		}

		ctrl.PlaceChildren()
	}
}

// ActiveColors return the attrubutes for the controls when it
// is active: text and background colors
func (c *BaseControl) ActiveColors() (term.Attribute, term.Attribute) {
	return c.fgActive, c.bgActive
}

// SetActiveTextColor changes text color of the active control
func (c *BaseControl) SetActiveTextColor(clr term.Attribute) {
	c.fgActive = clr
}

// SetActiveBackColor changes background color of the active control
func (c *BaseControl) SetActiveBackColor(clr term.Attribute) {
	c.bgActive = clr
}

func (c *BaseControl) removeChild(control Control) {
	children := []Control{}

	for _, child := range c.children {
		if child.RefID() == control.RefID() {
			continue
		}

		children = append(children, child)
	}
	c.children = nil

	for _, child := range children {
		c.AddChild(child)
	}
}

// Destroy removes an object from its parental chain
func (c *BaseControl) Destroy() {
	c.parent.removeChild(c)
}
<commit_msg>base: add OnActive() callback<commit_after>package clui

import (
	term "github.com/nsf/termbox-go"
	"sync"
	"sync/atomic"
)

// BaseControl is a base for all visible controls.
// Every new control must inherit it or implement
// the same set of methods
type BaseControl struct {
	refID         int64
	title         string
	x, y          int
	width, height int
	minW, minH    int
	scale         int
	fg, bg        term.Attribute
	fgActive      term.Attribute
	bgActive      term.Attribute
	tabSkip       bool
	disabled      bool
	hidden        bool
	align         Align
	parent        Control
	inactive      bool
	modal         bool
	padX, padY    int
	gapX, gapY    int
	pack          PackType
	children      []Control
	mtx           sync.RWMutex
	onActive      func(active bool)
}

var (
	globalRefId int64
)

func nextRefId() int64 {
	return atomic.AddInt64(&globalRefId, 1)
}

func NewBaseControl() BaseControl {
	return BaseControl{refID: nextRefId()}
}

func (c *BaseControl) RefID() int64 {
	return c.refID
}

func (c *BaseControl) Title() string {
	return c.title
}

func (c *BaseControl) SetTitle(title string) {
	c.title = title
}

func (c *BaseControl) Size() (widht int, height int) {
	return c.width, c.height
}

func (c *BaseControl) SetSize(width, height int) {
	if width < c.minW {
		width = c.minW
	}
	if height < c.minH {
		height = c.minH
	}

	if height != c.height || width != c.width {
		c.height = height
		c.width = width
	}
}

func (c *BaseControl) Pos() (x int, y int) {
	return c.x, c.y
}

func (c *BaseControl) SetPos(x, y int) {
	c.x = x
	c.y = y
}

func (c *BaseControl) applyConstraints() {
	ww, hh := c.width, c.height
	if ww < c.minW {
		ww = c.minW
	}
	if hh < c.minH {
		hh = c.minH
	}
	if hh != c.height || ww != c.width {
		c.SetSize(ww, hh)
	}
}

func (c *BaseControl) Constraints() (minw int, minh int) {
	return c.minW, c.minH
}

func (c *BaseControl) SetConstraints(minw, minh int) {
	c.minW = minw
	c.minH = minh
	c.applyConstraints()
}

func (c *BaseControl) Active() bool {
	return !c.inactive
}

func (c *BaseControl) SetActive(active bool) {
	c.inactive = !active

	if c.onActive != nil {
		c.onActive(active)
	}
}

func (c *BaseControl) OnActive(fn func(active bool)) {
	c.onActive = fn
}

func (c *BaseControl) TabStop() bool {
	return !c.tabSkip
}

func (c *BaseControl) SetTabStop(tabstop bool) {
	c.tabSkip = !tabstop
}

func (c *BaseControl) Enabled() bool {
	c.mtx.RLock()
	defer c.mtx.RUnlock()

	return !c.disabled
}

func (c *BaseControl) SetEnabled(enabled bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	c.disabled = !enabled
}

func (c *BaseControl) Visible() bool {
	c.mtx.RLock()
	defer c.mtx.RUnlock()

	return !c.hidden
}

func (c *BaseControl) SetVisible(visible bool) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	if visible == !c.hidden {
		return
	}

	c.hidden = !visible
	if c.parent == nil {
		return
	}

	p := c.Parent()
	for p.Parent() != nil {
		p = p.Parent()
	}

	go func() {
		if !c.inactive {
			PutEvent(Event{Type: EventKey, Key: term.KeyTab})
		}
		PutEvent(Event{Type: EventLayout, Target: p})
	}()
}

func (c *BaseControl) Parent() Control {
	return c.parent
}

func (c *BaseControl) SetParent(parent Control) {
	if c.parent == nil {
		c.parent = parent
	}
}

func (c *BaseControl) Modal() bool {
	return c.modal
}

func (c *BaseControl) SetModal(modal bool) {
	c.modal = modal
}

func (c *BaseControl) Paddings() (px int, py int) {
	return c.padX, c.padY
}

func (c *BaseControl) SetPaddings(px, py int) {
	if px >= 0 {
		c.padX = px
	}
	if py >= 0 {
		c.padY = py
	}
}

func (c *BaseControl) Gaps() (dx int, dy int) {
	return c.gapX, c.gapY
}

func (c *BaseControl) SetGaps(dx, dy int) {
	if dx >= 0 {
		c.gapX = dx
	}
	if dy >= 0 {
		c.gapY = dy
	}
}

func (c *BaseControl) Pack() PackType {
	return c.pack
}

func (c *BaseControl) SetPack(pack PackType) {
	c.pack = pack
}

func (c *BaseControl) Scale() int {
	return c.scale
}

func (c *BaseControl) SetScale(scale int) {
	if scale >= 0 {
		c.scale = scale
	}
}

func (c *BaseControl) Align() Align {
	return c.align
}

func (c *BaseControl) SetAlign(align Align) {
	c.align = align
}

func (c *BaseControl) TextColor() term.Attribute {
	return c.fg
}

func (c *BaseControl) SetTextColor(clr term.Attribute) {
	c.fg = clr
}

func (c *BaseControl) BackColor() term.Attribute {
	return c.bg
}

func (c *BaseControl) SetBackColor(clr term.Attribute) {
	c.bg = clr
}

func (c *BaseControl) childCount() int {
	cnt := 0
	for _, child := range c.children {
		if child.Visible() {
			cnt++
		}
	}

	return cnt
}

func (c *BaseControl) ResizeChildren() {
	children := c.childCount()
	if children == 0 {
		return
	}

	fullWidth := c.width - 2*c.padX
	fullHeight := c.height - 2*c.padY
	if c.pack == Horizontal {
		fullWidth -= (children - 1) * c.gapX
	} else {
		fullHeight -= (children - 1) * c.gapY
	}

	totalSc := c.ChildrenScale()
	minWidth := 0
	minHeight := 0
	for _, child := range c.children {
		if !child.Visible() {
			continue
		}

		cw, ch := child.MinimalSize()
		if c.pack == Horizontal {
			minWidth += cw
		} else {
			minHeight += ch
		}
	}

	aStep := 0
	diff := fullWidth - minWidth
	if c.pack == Vertical {
		diff = fullHeight - minHeight
	}
	if totalSc > 0 {
		aStep = int(float32(diff) / float32(totalSc))
	}

	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}

		tw, th := ctrl.MinimalSize()
		sc := ctrl.Scale()
		d := int(ctrl.Scale() * aStep)
		if c.pack == Horizontal {
			if sc != 0 {
				if sc == totalSc {
					tw += diff
					d = diff
				} else {
					tw += d
				}
			}
			th = fullHeight
		} else {
			if sc != 0 {
				if sc == totalSc {
					th += diff
					d = diff
				} else {
					th += d
				}
			}
			tw = fullWidth
		}
		diff -= d
		totalSc -= sc

		ctrl.SetSize(tw, th)
		ctrl.ResizeChildren()
	}
}

func (c *BaseControl) AddChild(control Control) {
	if c.children == nil {
		c.children = make([]Control, 1)
		c.children[0] = control
	} else {
		if c.ChildExists(control) {
			panic("Double adding a child")
		}

		c.children = append(c.children, control)
	}

	var ctrl Control
	var mainCtrl Control
	ctrl = c
	for ctrl != nil {
		ww, hh := ctrl.MinimalSize()
		cw, ch := ctrl.Size()
		if ww > cw || hh > ch {
			if ww > cw {
				cw = ww
			}
			if hh > ch {
				ch = hh
			}
			ctrl.SetConstraints(cw, ch)
		}

		if ctrl.Parent() == nil {
			mainCtrl = ctrl
		}
		ctrl = ctrl.Parent()
	}

	if mainCtrl != nil {
		mainCtrl.ResizeChildren()
		mainCtrl.PlaceChildren()
	}
}

func (c *BaseControl) Children() []Control {
	child := make([]Control, len(c.children))
	copy(child, c.children)
	return child
}

func (c *BaseControl) ChildExists(control Control) bool {
	if len(c.children) == 0 {
		return false
	}

	for _, ctrl := range c.children {
		if ctrl == control {
			return true
		}
	}

	return false
}

func (c *BaseControl) ChildrenScale() int {
	if c.childCount() == 0 {
		return c.scale
	}

	total := 0
	for _, ctrl := range c.children {
		if ctrl.Visible() {
			total += ctrl.Scale()
		}
	}

	return total
}

func (c *BaseControl) MinimalSize() (w int, h int) {
	children := c.childCount()
	if children == 0 {
		return c.minW, c.minH
	}

	totalX := 2 * c.padX
	totalY := 2 * c.padY

	if c.pack == Vertical {
		totalY += (children - 1) * c.gapY
	} else {
		totalX += (children - 1) * c.gapX
	}

	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}
		ww, hh := ctrl.MinimalSize()
		if c.pack == Vertical {
			totalY += hh
			if ww+2*c.padX > totalX {
				totalX = ww + 2*c.padX
			}
		} else {
			totalX += ww
			if hh+2*c.padY > totalY {
				totalY = hh + 2*c.padY
			}
		}
	}

	if totalX < c.minW {
		totalX = c.minW
	}
	if totalY < c.minH {
		totalY = c.minH
	}

	return totalX, totalY
}

func (c *BaseControl) Draw() {
	panic("BaseControl Draw Called")
}

func (c *BaseControl) DrawChildren() {
	if c.hidden {
		return
	}

	PushClip()
	defer PopClip()

	SetClipRect(c.x+c.padX, c.y+c.padY, c.width-2*c.padX, c.height-2*c.padY)

	for _, child := range c.children {
		child.Draw()
	}
}

func (c *BaseControl) HitTest(x, y int) HitResult {
	if x > c.x && x < c.x+c.width-1 &&
		y > c.y && y < c.y+c.height-1 {
		return HitInside
	}

	if (x == c.x || x == c.x+c.width-1) &&
		y >= c.y && y < c.y+c.height {
		return HitBorder
	}

	if (y == c.y || y == c.y+c.height-1) &&
		x >= c.x && x < c.x+c.width {
		return HitBorder
	}

	return HitOutside
}

func (c *BaseControl) ProcessEvent(ev Event) bool {
	return SendEventToChild(c, ev)
}

func (c *BaseControl) PlaceChildren() {
	children := c.childCount()
	if c.children == nil || children == 0 {
		return
	}

	xx, yy := c.x+c.padX, c.y+c.padY
	for _, ctrl := range c.children {
		if !ctrl.Visible() {
			continue
		}

		ctrl.SetPos(xx, yy)
		ww, hh := ctrl.Size()
		if c.pack == Vertical {
			yy += c.gapY + hh
		} else {
			xx += c.gapX + ww
		}

		ctrl.PlaceChildren()
	}
}

// ActiveColors return the attrubutes for the controls when it
// is active: text and background colors
func (c *BaseControl) ActiveColors() (term.Attribute, term.Attribute) {
	return c.fgActive, c.bgActive
}

// SetActiveTextColor changes text color of the active control
func (c *BaseControl) SetActiveTextColor(clr term.Attribute) {
	c.fgActive = clr
}

// SetActiveBackColor changes background color of the active control
func (c *BaseControl) SetActiveBackColor(clr term.Attribute) {
	c.bgActive = clr
}

func (c *BaseControl) removeChild(control Control) {
	children := []Control{}

	for _, child := range c.children {
		if child.RefID() == control.RefID() {
			continue
		}

		children = append(children, child)
	}
	c.children = nil

	for _, child := range children {
		c.AddChild(child)
	}
}

// Destroy removes an object from its parental chain
func (c *BaseControl) Destroy() {
	c.parent.removeChild(c)
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/diyan/assimilator/db"
	"github.com/labstack/echo"
	"github.com/pkg/errors"
)

// MustGetProjectID is like GetProjectID but panics if `organization_slug`
//  or `project_slug` was not passed in HTTP request
// TODO context_helpers a bad name, later we need to split this module into the
//  several modules with good names.
func MustGetProjectID(c echo.Context) int64 {
	projectID, err := GetProjectID(c)
	if err != nil {
		panic(errors.Wrap(err, "can not get project"))
	}
	return projectID
}

// GetProjectID returns projectID for current HTTP request
// TODO add argument, so we can return all projects or
//   projects that only visible to current user
func GetProjectID(c echo.Context) (int64, error) {
	orgSlug := c.Param("organization_slug")
	projectSlug := c.Param("project_slug")
	db, err := db.GetTx(c)
	if err != nil {
		return 0, err
	}
	projectID, err := db.SelectBySql(`
		select p.id
			from sentry_project p
				join sentry_organization o on p.organization_id = o.id
		where o.slug = ? and p.slug = ?`,
		orgSlug, projectSlug).
		ReturnInt64()
	return projectID, errors.Wrap(err, "query failed")
	// TODO return ResourceDoesNotExist if record was not found
	// TODO check project permissions -> self.check_object_permissions(request, project)
}
<commit_msg>Optimize error handling in context helpers<commit_after>package api

import (
	"github.com/diyan/assimilator/db"
	"github.com/labstack/echo"
	"github.com/pkg/errors"
)

// MustGetProjectID is like GetProjectID but panics if `organization_slug`
//  or `project_slug` was not passed in HTTP request
// TODO context_helpers a bad name, later we need to split this module into the
//  several modules with good names.
func MustGetProjectID(c echo.Context) int64 {
	projectID, err := GetProjectID(c)
	if err != nil {
		panic(err)
	}
	return projectID
}

// GetProjectID returns projectID for current HTTP request
// TODO add argument, so we can return all projects or
//   projects that only visible to current user
func GetProjectID(c echo.Context) (int64, error) {
	orgSlug := c.Param("organization_slug")
	projectSlug := c.Param("project_slug")
	db, err := db.GetTx(c)
	if err != nil {
		return 0, err
	}
	// TODO validate that orgSlug is exists
	projectID, err := db.SelectBySql(`
		select p.id
			from sentry_project p
				join sentry_organization o on p.organization_id = o.id
		where o.slug = ? and p.slug = ?`,
		orgSlug, projectSlug).
		ReturnInt64()
	return projectID, errors.Wrap(err, "can not get project")
	// TODO return ResourceDoesNotExist if record was not found
	// TODO check project permissions -> self.check_object_permissions(request, project)
}
<|endoftext|>


<commit_before>package bloom

import (
	"fmt"
	"testing"
)

func TestNewBloom(t *testing.T) {
	s := NewBloom(64, 1)
	if s.k != 1 {
		t.Errorf("NewBloom returned wrong hashes for filter")
	}
	if s.m != 64 {
		t.Errorf("NewBloom returned wrong filter size")
	}
	if cap(s.collection) != 1 {
		t.Errorf("NewBloom built wrong sized collection of uint64s")
	}
}

func TestJustTest(t *testing.T) {
	b := NewBloom(128, 1)
	if b.Test([]byte("test")) {
		t.Errorf("Insert or Test broken")
	}
}

func TestInsertAndTest(t *testing.T) {
	b := NewBloom(128, 1)
	b.hasher = func (b *Bloom, value []byte) ([]uint64) {
		return make([]uint64, 1)
	}
	b.Insert([]byte("test"))
	if !b.Test([]byte("test")) {
		t.Errorf("Insert or Test broken")
	}
}

func TestAvalanche(t *testing.T) {
	rawWord := "testing 123 testing test test yes"
	word := []byte(rawWord)

	a1, a2 := murmur3_128(word)

	totalBits := 0
	matchBits := 0

	for k := range word {
		for i := 0; i < 8; i++ {
			wordx := []byte(rawWord)
			wordx[k] ^= (1 << uint(i))

			// now word and wordx have one bit set differently

			b1, b2 := murmur3_128(wordx)

			c1 := a1 ^ b1
			c2 := a2 ^ b2

			for m := 0; m < 64; m++ {
				totalBits += 1
				matchBits += int(c1 & 1)
				c1 >>= 1
			}
			for m := 0; m < 64; m++ {
				totalBits += 1
				matchBits += int(c2 & 1)
				c2 >>= 1
			}
		}
	}
	fmt.Println("total", matchBits, totalBits, 100.0 * float64(matchBits) / float64(totalBits))
}

<commit_msg>wrote a much better avalanche test<commit_after>package bloom

import (
	"math/rand"
	"testing"
)

func TestNewBloom(t *testing.T) {
	s := NewBloom(64, 1)
	if s.k != 1 {
		t.Errorf("NewBloom returned wrong hashes for filter")
	}
	if s.m != 64 {
		t.Errorf("NewBloom returned wrong filter size")
	}
	if cap(s.collection) != 1 {
		t.Errorf("NewBloom built wrong sized collection of uint64s")
	}
}

func TestJustTest(t *testing.T) {
	b := NewBloom(128, 1)
	if b.Test([]byte("test")) {
		t.Errorf("Insert or Test broken")
	}
}

func TestInsertAndTest(t *testing.T) {
	b := NewBloom(128, 1)
	b.hasher = func(b *Bloom, value []byte) []uint64 {
		return make([]uint64, 1)
	}
	b.Insert([]byte("test"))
	if !b.Test([]byte("test")) {
		t.Errorf("Insert or Test broken")
	}
}

func TestAvalanche(t *testing.T) {
	wordLen := 32
	pairs := 1000000

	// let's build pairs comprised of a random hash key and its copy with a single bit twiddled!

	inputs := make([][]byte, pairs)
	tweaked := make([][]byte, pairs)
	inputsc := make([][]uint64, pairs)
	tweakedc := make([][]uint64, pairs)
	avalanche := make([][]uint64, pairs)

	for i := 0; i < pairs; i++ {
		inputs[i] = make([]byte, wordLen)
		tweaked[i] = make([]byte, wordLen)
		inputsc[i] = make([]uint64, 2)
		tweakedc[i] = make([]uint64, 2)
		avalanche[i] = make([]uint64, 2)
		for j := 0; j < wordLen; j++ {
			inputs[i][j] = byte(rand.Int())
			tweaked[i][j] = inputs[i][j]
		}
		tweaked[i][rand.Intn(wordLen)] ^= (1 << uint(rand.Intn(8)))
		inputsc[i][0], inputsc[i][1] = murmur3_128(inputs[i])
		tweakedc[i][0], tweakedc[i][1] = murmur3_128(tweaked[i])

		avalanche[i][0] = inputsc[i][0] ^ tweakedc[i][0]
		avalanche[i][1] = inputsc[i][1] ^ tweakedc[i][1]
		// fmt.Printf("%64.64b%64.64b\n\n\n", avalanche[i][0], avalanche[i][1])
	}

	B := make([]uint64, pairs)

	for i := 0; i < pairs; i++ {
		for j := 0; j < 2; j++ {
			c := avalanche[i][j]
			for m := 0; m < 64; m++ {
				B[i] += (c & 1)
				c >>= 1
			}
		}
	}

	// For each experiment/trial, we've counted the number of avalanche bits.
	// Each is equivalent to 128 coin flips, so we can use Fisher's Method
	// and some simple probability to find upper and lower bounds on the
	// number of 1-bit per experiment. Because they're a series of coin
	// flips, we know they ought to be distributed approximately normally,
	// and that the probability of exceeding our chosen threshold is a
	// two-tailed p-value is:
	//   2 * 1/2^128 * sum (128 choose (128-j)), j=0 to 32 ~= 1.28418*10^-8
	// I picked 32 because it has the nice property of being 1/4 of 128,
	// which means any 1-bit or 0-bit count that exceeds half of the total
	// bits implies that we reject the hypothesis.

	var minOnes uint64 = 32
	var maxOnes uint64 = 96

	for i := 0; i < pairs; i++ {
		if B[i] > maxOnes || B[i] < minOnes {
			t.Errorf("Hash failed Avalanche test with %v bits\n", B[i])
		}
	}
}
<|endoftext|>


<commit_before>package routes

import (
	"encoding/json"
	"github.com/danjac/photoshare/api/models"
	"github.com/danjac/photoshare/api/session"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"runtime/debug"
)

type AppContext struct {
	*http.Request
	Response http.ResponseWriter
	Params   map[string]string
	User     *models.User
}

func (c *AppContext) Param(name string) string {
	return c.Params[name]
}

func (c *AppContext) GetCurrentUser() (*models.User, error) {
	var err error
	c.User, err = session.GetCurrentUser(c.Request)
	return c.User, err
}

func (c *AppContext) Login(user *models.User) error {
	return session.Login(c.Response, user)
}

func (c *AppContext) Logout() error {
	return session.Logout(c.Response)
}

func (c *AppContext) Render(status int, value interface{}) error {
	c.Response.WriteHeader(status)
	c.Response.Header().Set("Content-type", "application/json")
	return json.NewEncoder(c.Response).Encode(value)
}

func (c *AppContext) OK(value interface{}) error {
	return c.Render(http.StatusOK, value)
}

func (c *AppContext) Unauthorized(value interface{}) error {
	return c.Render(http.StatusUnauthorized, value)
}

func (c *AppContext) Forbidden(value interface{}) error {
	return c.Render(http.StatusForbidden, value)
}

func (c *AppContext) BadRequest(value interface{}) error {
	return c.Render(http.StatusBadRequest, value)
}

func (c *AppContext) NotFound(value interface{}) error {
	return c.Render(http.StatusNotFound, value)
}

func (c *AppContext) ParseJSON(value interface{}) error {
	return json.NewDecoder(c.Request.Body).Decode(value)
}

func (c *AppContext) Error(err error) {
	stack := debug.Stack()
	log.Printf("ERROR: %s\n%s\n%s", c.Request, err, stack)
	http.Error(c.Response, "Sorry, an error has occurred", http.StatusInternalServerError)
}

func NewAppContext(w http.ResponseWriter, r *http.Request) *AppContext {
	return &AppContext{r, w, mux.Vars(r), nil}
}

func MakeAppHandler(fn AppHandlerFunc, loginRequired bool) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		c := NewAppContext(w, r)
		if loginRequired {
			if user, err := c.GetCurrentUser(); err != nil || user == nil {
				if err != nil {
					c.Error(err)
					return
				}
				c.Unauthorized("You must be logged in")
				return
			}
		}

		if err := fn(c); err != nil {
			c.Error(err)
		}
	}

}

type AppHandlerFunc func(c *AppContext) error
<commit_msg>Remove unneeded struct<commit_after>package routes

import (
	"encoding/json"
	"github.com/danjac/photoshare/api/models"
	"github.com/danjac/photoshare/api/session"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"runtime/debug"
)

type AppContext struct {
	*http.Request
	Response http.ResponseWriter
	Params   map[string]string
	User     *models.User
}

func (c *AppContext) Param(name string) string {
	return c.Params[name]
}

func (c *AppContext) GetCurrentUser() (*models.User, error) {
	var err error
	c.User, err = session.GetCurrentUser(c.Request)
	return c.User, err
}

func (c *AppContext) Login(user *models.User) error {
    c.User = user
	return session.Login(c.Response, user)
}

func (c *AppContext) Logout() error {
    c.User = nil
	return session.Logout(c.Response)
}

func (c *AppContext) Render(status int, value interface{}) error {
	c.Response.WriteHeader(status)
	c.Response.Header().Set("Content-type", "application/json")
	return json.NewEncoder(c.Response).Encode(value)
}

func (c *AppContext) OK(value interface{}) error {
	return c.Render(http.StatusOK, value)
}

func (c *AppContext) Unauthorized(value interface{}) error {
	return c.Render(http.StatusUnauthorized, value)
}

func (c *AppContext) Forbidden(value interface{}) error {
	return c.Render(http.StatusForbidden, value)
}

func (c *AppContext) BadRequest(value interface{}) error {
	return c.Render(http.StatusBadRequest, value)
}

func (c *AppContext) NotFound(value interface{}) error {
	return c.Render(http.StatusNotFound, value)
}

func (c *AppContext) ParseJSON(value interface{}) error {
	return json.NewDecoder(c.Request.Body).Decode(value)
}

func (c *AppContext) Error(err error) {
	stack := debug.Stack()
	log.Printf("ERROR: %s\n%s\n%s", c.Request, err, stack)
	http.Error(c.Response, "Sorry, an error has occurred", http.StatusInternalServerError)
}

func NewAppContext(w http.ResponseWriter, r *http.Request) *AppContext {
	return &AppContext{r, w, mux.Vars(r), nil}
}

func MakeAppHandler(fn AppHandlerFunc, loginRequired bool) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		c := NewAppContext(w, r)
		if loginRequired {
			if user, err := c.GetCurrentUser(); err != nil || user == nil {
				if err != nil {
					c.Error(err)
					return
				}
				c.Unauthorized("You must be logged in")
				return
			}
		}

		if err := fn(c); err != nil {
			c.Error(err)
		}
	}

}

type AppHandlerFunc func(c *AppContext) error
<|endoftext|>


<commit_before>package backup

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

	"github.com/spf13/cobra"
	"github.com/zero-os/0-Disk/log"
	"github.com/zero-os/0-Disk/nbd/ardb/backup"

	cmdconfig "github.com/zero-os/0-Disk/zeroctl/cmd/config"
)

// DescribeSnapshotCmd represents the describe-snapshot subcommand
var DescribeSnapshotCmd = &cobra.Command{
	Use:   "snapshot snapshotID",
	Short: "describe a snapshot",
	RunE:  describeSnapshot,
}

// describe only configuration
// see `init` for more information
// about the meaning of each config property.
var describeVdiskCmdCfg struct {
	PrettyPrint bool
}

func describeSnapshot(cmd *cobra.Command, args []string) error {
	logLevel := log.ErrorLevel
	if cmdconfig.Verbose {
		logLevel = log.DebugLevel
	}
	log.SetLevel(logLevel)

	// parse the position arguments
	err := parseDescribePosArguments(args)
	if err != nil {
		return err
	}

	// create (backup) storage driver (so we can read snapshot header from it)
	driver, err := backup.NewStorageDriver(vdiskCmdCfg.BackupStorageConfig)
	if err != nil {
		return err
	}

	// load snapshot's header
	header, err := backup.LoadHeader(
		vdiskCmdCfg.SnapshotID,
		driver,
		&vdiskCmdCfg.PrivateKey,
		vdiskCmdCfg.CompressionType)
	if err != nil {
		return err
	}

	var info SnapshotInfo

	info.SnapshotID = header.Metadata.SnapshotID
	info.BlockSize = header.Metadata.BlockSize
	info.Size = info.BlockSize * header.DedupedMap.Count
	info.Created = header.Metadata.Created

	info.Source.VdiskID = header.Metadata.Source.VdiskID
	info.Source.BlockSize = header.Metadata.Source.BlockSize
	info.Source.Size = header.Metadata.Source.Size

	var bytes []byte
	if describeVdiskCmdCfg.PrettyPrint {
		bytes, err = json.MarshalIndent(info, "", "  \t")
	} else {
		bytes, err = json.Marshal(info)
	}
	if err != nil {
		return err
	}

	fmt.Println(string(bytes))
	return nil // TODO
}

// SnapshotInfo describes a snapshot,
// using both required and optional information.
type SnapshotInfo struct {
	SnapshotID string `json:"snapshotID"`
	BlockSize  int64  `json:"blockSize"`
	Size       int64  `json:"size"`
	Created    string `json:"created,omitempty"`
	Source     struct {
		VdiskID   string `json:"vdiskID,omitempty"`
		BlockSize int64  `json:"blockSize,omitempty"`
		Size      int64  `json:"size,omitempty"`
	} `json:"source,omitempty"`
}

func parseDescribePosArguments(args []string) error {
	// validate pos arg length
	argn := len(args)
	if argn < 1 {
		return errors.New("not enough arguments")
	} else if argn > 1 {
		return errors.New("too many arguments")
	}

	vdiskCmdCfg.SnapshotID = args[0]
	return nil
}

func init() {
	DescribeSnapshotCmd.Long = DescribeSnapshotCmd.Short + `

Remember to use the same (snapshot) name,
crypto (private) key and the compression type,
as you used while exporting the backup in question.

The crypto (private) key has a required fixed length of 32 bytes.
If the snapshot wasn't encrypted, no key should be given,
giving a key in this scenario will fail the describe.

  The FTP information is given using the --storage flag,
here are some examples of valid values for that flag:
	+ localhost:22
	+ ftp://1.2.3.4:200
	+ ftp://user@127.0.0.1:200
	+ ftp://user:pass@12.30.120.200:3000

Alternatively you can also give a local directory path to the --storage flag,
to backup to the local file system instead.
This is also the default in case the --storage flag is not specified.
`

	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.CompressionType, "compression", "c",
		"the compression type to use, options { lz4, xz }")
	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.PrivateKey, "key", "k",
		"an optional 32 byte fixed-size private key used for encryption when given")

	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.BackupStorageConfig, "storage", "s",
		"ftp server url or local dir path to read the snapshot's header from")

	DescribeSnapshotCmd.Flags().BoolVar(
		&describeVdiskCmdCfg.PrettyPrint, "pretty", false,
		"pretty print output when this flag is specified")
}
<commit_msg>omit source from backup description if not given<commit_after>package backup

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

	"github.com/spf13/cobra"
	"github.com/zero-os/0-Disk/log"
	"github.com/zero-os/0-Disk/nbd/ardb/backup"

	cmdconfig "github.com/zero-os/0-Disk/zeroctl/cmd/config"
)

// DescribeSnapshotCmd represents the describe-snapshot subcommand
var DescribeSnapshotCmd = &cobra.Command{
	Use:   "snapshot snapshotID",
	Short: "describe a snapshot",
	RunE:  describeSnapshot,
}

// describe only configuration
// see `init` for more information
// about the meaning of each config property.
var describeVdiskCmdCfg struct {
	PrettyPrint bool
}

func describeSnapshot(cmd *cobra.Command, args []string) error {
	logLevel := log.ErrorLevel
	if cmdconfig.Verbose {
		logLevel = log.DebugLevel
	}
	log.SetLevel(logLevel)

	// parse the position arguments
	err := parseDescribePosArguments(args)
	if err != nil {
		return err
	}

	// create (backup) storage driver (so we can read snapshot header from it)
	driver, err := backup.NewStorageDriver(vdiskCmdCfg.BackupStorageConfig)
	if err != nil {
		return err
	}

	// load snapshot's header
	header, err := backup.LoadHeader(
		vdiskCmdCfg.SnapshotID,
		driver,
		&vdiskCmdCfg.PrivateKey,
		vdiskCmdCfg.CompressionType)
	if err != nil {
		return err
	}

	var info SnapshotInfo

	info.SnapshotID = header.Metadata.SnapshotID
	info.BlockSize = header.Metadata.BlockSize
	info.Size = info.BlockSize * header.DedupedMap.Count
	info.Created = header.Metadata.Created

	if header.Metadata.Source.VdiskID != "" {
		info.Source = &SnapshotSourceInfo{
			VdiskID:   header.Metadata.Source.VdiskID,
			BlockSize: header.Metadata.Source.BlockSize,
			Size:      header.Metadata.Source.Size,
		}
	}

	var bytes []byte
	if describeVdiskCmdCfg.PrettyPrint {
		bytes, err = json.MarshalIndent(info, "", "  \t")
	} else {
		bytes, err = json.Marshal(info)
	}
	if err != nil {
		return err
	}

	fmt.Println(string(bytes))
	return nil // TODO
}

// SnapshotInfo describes a snapshot,
// using both required and optional information.
type SnapshotInfo struct {
	SnapshotID string              `json:"snapshotID"`
	BlockSize  int64               `json:"blockSize"`
	Size       int64               `json:"size"`
	Created    string              `json:"created,omitempty"`
	Source     *SnapshotSourceInfo `json:"source,omitempty"`
}

// SnapshotSourceInfo describes optional information about
// the source of a snapshot.
type SnapshotSourceInfo struct {
	VdiskID   string `json:"vdiskID,omitempty"`
	BlockSize int64  `json:"blockSize,omitempty"`
	Size      int64  `json:"size,omitempty"`
}

func parseDescribePosArguments(args []string) error {
	// validate pos arg length
	argn := len(args)
	if argn < 1 {
		return errors.New("not enough arguments")
	} else if argn > 1 {
		return errors.New("too many arguments")
	}

	vdiskCmdCfg.SnapshotID = args[0]
	return nil
}

func init() {
	DescribeSnapshotCmd.Long = DescribeSnapshotCmd.Short + `

Remember to use the same (snapshot) name,
crypto (private) key and the compression type,
as you used while exporting the backup in question.

The crypto (private) key has a required fixed length of 32 bytes.
If the snapshot wasn't encrypted, no key should be given,
giving a key in this scenario will fail the describe.

  The FTP information is given using the --storage flag,
here are some examples of valid values for that flag:
	+ localhost:22
	+ ftp://1.2.3.4:200
	+ ftp://user@127.0.0.1:200
	+ ftp://user:pass@12.30.120.200:3000

Alternatively you can also give a local directory path to the --storage flag,
to backup to the local file system instead.
This is also the default in case the --storage flag is not specified.
`

	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.CompressionType, "compression", "c",
		"the compression type to use, options { lz4, xz }")
	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.PrivateKey, "key", "k",
		"an optional 32 byte fixed-size private key used for encryption when given")

	DescribeSnapshotCmd.Flags().VarP(
		&vdiskCmdCfg.BackupStorageConfig, "storage", "s",
		"ftp server url or local dir path to read the snapshot's header from")

	DescribeSnapshotCmd.Flags().BoolVar(
		&describeVdiskCmdCfg.PrettyPrint, "pretty", false,
		"pretty print output when this flag is specified")
}
<|endoftext|>


<commit_before>package webhook

import (
	"errors"
	"regexp"
	"strings"
)

const (
	// AttachmentColorGood define good color (green) for color fields of attachment
	AttachmentColorGood = "good"
	// AttachmentColorWarning define warning color (yellow) for color fields of attachment
	AttachmentColorWarning = "warning"
	// AttachmentColorDanger define danger color (red) for color fields of attachment
	AttachmentColorDanger = "danger"
)

var (
	// supportedColorName contains list of named color that are supported by Slack
	supportedColorName = map[string]bool{
		AttachmentColorGood:    true,
		AttachmentColorWarning: true,
		AttachmentColorDanger:  true,
	}

	// colorRegex to check if hex color is valid
	colorRegex = regexp.MustCompile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")

	// ErrColorInvalid return when set invalid color to attachment
	ErrColorInvalid = errors.New("inputed color is invalid, it should be 'good', 'warning', 'danger' or a hex color")
)

// Field represent a field object in attachment fields array,
// see https://api.slack.com/docs/attachments
type Field struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// NewField create new Field with given params
func NewField(title, value string) *Field {
	return &Field{
		Title: title,
		Value: value,
	}
}

// Attachment represent an attachment object,
// see https://api.slack.com/docs/attachments
type Attachment struct {
	Fallback   string   `json:"fallback,omitempty"`
	Color      string   `json:"color,omitemtpy"`
	PreText    string   `json:"pretext,omitemtpy"`
	AuthorName string   `json:"author_name,omitemtpy"`
	AuthorLink string   `json:"author_link,omitemtpy"`
	AuthorIcon string   `json:"author_icon,omitemtpy"`
	Title      string   `json:"title,omitemtpy"`
	TitleLink  string   `json:"title_link,omitemtpy"`
	Text       string   `json:"text,omitemtpy"`
	ImageURL   string   `json:"image_url,omitemtpy"`
	ThumbURL   string   `json:"thumb_url,omitemtpy"`
	Fields     []*Field `json:"fields,omitemtpy"`
	MrkDwnIn   []string `json:"mrkdwn_in"`
}

// NewAttachment create new attachment object, with good color by default
func NewAttachment(text, title, titleURL string) *Attachment {
	attachment := &Attachment{}
	attachment.Text = text
	attachment.Title = title
	attachment.TitleLink = titleURL

	attachment.SetColorToGood()
	attachment.SetMarkDownFields("pretext", "text", "fields")
	return attachment
}

// SetMarkDownFields set fields which are in MarkDown format,
// note that it will overwrite previous values.
// Valid values: "pretext", "text", "fields".
func (a *Attachment) SetMarkDownFields(fields ...string) {
	a.MrkDwnIn = fields
}

// AddMarkDownField will append new field to current list of markdown fields.
// Valid values: "pretext", "text", "fields".
func (a *Attachment) AddMarkDownField(field string) {
	a.MrkDwnIn = append(a.MrkDwnIn, field)
}

// AddField will append an attachment field to list of fields
func (a *Attachment) AddField(title, value string) {
	a.Fields = append(a.Fields, NewField(title, value))
}

// AddShortField will append an attachment field with short is true to list of fields
func (a *Attachment) AddShortField(title, value string) {
	field := NewField(title, value)
	field.Short = true
	a.Fields = append(a.Fields, field)
}

// SetColor is used to set color to field color of attachment,
// color can 'good', 'warning', 'danger' or a hex color
func (a *Attachment) SetColor(color string) error {
	if strings.HasPrefix(color, "#") {
		if !colorRegex.MatchString(color) {
			return ErrColorInvalid
		}
	} else {
		if _, found := supportedColorName[color]; !found {
			return ErrColorInvalid
		}
	}

	a.Color = color
	return nil
}

// SetColorToGood will set color to green color
func (a *Attachment) SetColorToGood() {
	a.SetColor(AttachmentColorGood)
}

// SetColorToWarning will set color to yellow color
func (a *Attachment) SetColorToWarning() {
	a.SetColor(AttachmentColorWarning)
}

// SetColorToDanger will set color to red color
func (a *Attachment) SetColorToDanger() {
	a.SetColor(AttachmentColorDanger)
}
<commit_msg>[webhook] Do not return MrkDwnIn if it is emtpy.<commit_after>package webhook

import (
	"errors"
	"regexp"
	"strings"
)

const (
	// AttachmentColorGood define good color (green) for color fields of attachment
	AttachmentColorGood = "good"
	// AttachmentColorWarning define warning color (yellow) for color fields of attachment
	AttachmentColorWarning = "warning"
	// AttachmentColorDanger define danger color (red) for color fields of attachment
	AttachmentColorDanger = "danger"
)

var (
	// supportedColorName contains list of named color that are supported by Slack
	supportedColorName = map[string]bool{
		AttachmentColorGood:    true,
		AttachmentColorWarning: true,
		AttachmentColorDanger:  true,
	}

	// colorRegex to check if hex color is valid
	colorRegex = regexp.MustCompile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")

	// ErrColorInvalid return when set invalid color to attachment
	ErrColorInvalid = errors.New("inputed color is invalid, it should be 'good', 'warning', 'danger' or a hex color")
)

// Field represent a field object in attachment fields array,
// see https://api.slack.com/docs/attachments
type Field struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// NewField create new Field with given params
func NewField(title, value string) *Field {
	return &Field{
		Title: title,
		Value: value,
	}
}

// Attachment represent an attachment object,
// see https://api.slack.com/docs/attachments
type Attachment struct {
	Fallback   string   `json:"fallback,omitempty"`
	Color      string   `json:"color,omitemtpy"`
	PreText    string   `json:"pretext,omitemtpy"`
	AuthorName string   `json:"author_name,omitemtpy"`
	AuthorLink string   `json:"author_link,omitemtpy"`
	AuthorIcon string   `json:"author_icon,omitemtpy"`
	Title      string   `json:"title,omitemtpy"`
	TitleLink  string   `json:"title_link,omitemtpy"`
	Text       string   `json:"text,omitemtpy"`
	ImageURL   string   `json:"image_url,omitemtpy"`
	ThumbURL   string   `json:"thumb_url,omitemtpy"`
	Fields     []*Field `json:"fields,omitemtpy"`
	MrkDwnIn   []string `json:"mrkdwn_in,omitempty"`
}

// NewAttachment create new attachment object, with good color by default
func NewAttachment(text, title, titleURL string) *Attachment {
	attachment := &Attachment{}
	attachment.Text = text
	attachment.Title = title
	attachment.TitleLink = titleURL

	attachment.SetColorToGood()
	attachment.SetMarkDownFields("pretext", "text", "fields")
	return attachment
}

// SetMarkDownFields set fields which are in MarkDown format,
// note that it will overwrite previous values.
// Valid values: "pretext", "text", "fields".
func (a *Attachment) SetMarkDownFields(fields ...string) {
	a.MrkDwnIn = fields
}

// AddMarkDownField will append new field to current list of markdown fields.
// Valid values: "pretext", "text", "fields".
func (a *Attachment) AddMarkDownField(field string) {
	a.MrkDwnIn = append(a.MrkDwnIn, field)
}

// AddField will append an attachment field to list of fields
func (a *Attachment) AddField(title, value string) {
	a.Fields = append(a.Fields, NewField(title, value))
}

// AddShortField will append an attachment field with short is true to list of fields
func (a *Attachment) AddShortField(title, value string) {
	field := NewField(title, value)
	field.Short = true
	a.Fields = append(a.Fields, field)
}

// SetColor is used to set color to field color of attachment,
// color can 'good', 'warning', 'danger' or a hex color
func (a *Attachment) SetColor(color string) error {
	if strings.HasPrefix(color, "#") {
		if !colorRegex.MatchString(color) {
			return ErrColorInvalid
		}
	} else {
		if _, found := supportedColorName[color]; !found {
			return ErrColorInvalid
		}
	}

	a.Color = color
	return nil
}

// SetColorToGood will set color to green color
func (a *Attachment) SetColorToGood() {
	a.SetColor(AttachmentColorGood)
}

// SetColorToWarning will set color to yellow color
func (a *Attachment) SetColorToWarning() {
	a.SetColor(AttachmentColorWarning)
}

// SetColorToDanger will set color to red color
func (a *Attachment) SetColorToDanger() {
	a.SetColor(AttachmentColorDanger)
}
<|endoftext|>


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

// +build appengine

package build

import (
	"context"
	"net/http"
	"strings"

	"google.golang.org/appengine"
)

func handleFunc(path string, h http.HandlerFunc) {
	for _, d := range dashboards {
		http.Handle(d.Prefix+path, hstsHandler(h))
	}
}

// hstsHandler wraps an http.HandlerFunc such that it sets the HSTS header.
func hstsHandler(fn http.HandlerFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
		fn(w, r)
	})
}

// Dashboard describes a unique build dashboard.
type Dashboard struct {
	Name      string     // This dashboard's name (eg, "Go")
	Namespace string     // This dashboard's namespace (eg, "" (default), "Git")
	Prefix    string     // The path prefix (no trailing /)
	Packages  []*Package // The project's packages to build
}

// dashboardForRequest returns the appropriate dashboard for a given URL path.
func dashboardForRequest(r *http.Request) *Dashboard {
	for _, d := range dashboards[1:] {
		if d.Prefix == "" {
			panic("prefix can be empty only for the first dashboard")
		}
		if strings.HasPrefix(r.URL.Path, d.Prefix) {
			return d
		}
	}
	if dashboards[0].Prefix != "" {
		panic("prefix for the first dashboard should be empty")
	}
	return dashboards[0]
}

// Context returns a namespaced context for this dashboard, or panics if it
// fails to create a new context.
func (d *Dashboard) Context(c context.Context) context.Context {
	if d.Namespace == "" {
		return c
	}
	n, err := appengine.Namespace(c, d.Namespace)
	if err != nil {
		panic(err)
	}
	return n
}

// The currently known dashboards.
// The first one should have an empty prefix and
// the other ones a non empty prefix.
var dashboards = []*Dashboard{goDash, gccgoDash}

// goDash is the dashboard for the main go repository.
var goDash = &Dashboard{
	Name:      "Go",
	Namespace: "Git",
	Prefix:    "",
	Packages:  goPackages,
}

// goPackages is a list of all of the packages built by the main go repository.
var goPackages = []*Package{
	{
		Kind: "go",
		Name: "Go",
	},
	{
		Kind: "subrepo",
		Name: "arch",
		Path: "golang.org/x/arch",
	},
	{
		Kind: "subrepo",
		Name: "benchmarks",
		Path: "golang.org/x/benchmarks",
	},
	{
		Kind: "subrepo",
		Name: "blog",
		Path: "golang.org/x/blog",
	},
	{
		Kind: "subrepo",
		Name: "crypto",
		Path: "golang.org/x/crypto",
	},
	{
		Kind: "subrepo",
		Name: "debug",
		Path: "golang.org/x/debug",
	},
	{
		Kind: "subrepo",
		Name: "exp",
		Path: "golang.org/x/exp",
	},
	{
		Kind: "subrepo",
		Name: "image",
		Path: "golang.org/x/image",
	},
	{
		Kind: "subrepo",
		Name: "mobile",
		Path: "golang.org/x/mobile",
	},
	{
		Kind: "subrepo",
		Name: "net",
		Path: "golang.org/x/net",
	},
	{
		Kind: "subrepo",
		Name: "oauth2",
		Path: "golang.org/x/oauth2",
	},
	{
		Kind: "subrepo",
		Name: "perf",
		Path: "golang.org/x/perf",
	},
	{
		Kind: "subrepo",
		Name: "review",
		Path: "golang.org/x/review",
	},
	{
		Kind: "subrepo",
		Name: "sync",
		Path: "golang.org/x/sync",
	},
	{
		Kind: "subrepo",
		Name: "sys",
		Path: "golang.org/x/sys",
	},
	{
		Kind: "subrepo",
		Name: "talks",
		Path: "golang.org/x/talks",
	},
	{
		Kind: "subrepo",
		Name: "term",
		Path: "golang.org/x/term",
	},
	{
		Kind: "subrepo",
		Name: "text",
		Path: "golang.org/x/text",
	},
	{
		Kind: "subrepo",
		Name: "time",
		Path: "golang.org/x/time",
	},
	{
		Kind: "subrepo",
		Name: "tools",
		Path: "golang.org/x/tools",
	},
	{
		Kind: "subrepo",
		Name: "tour",
		Path: "golang.org/x/tour",
	},
}

// gccgoDash is the dashboard for gccgo.
var gccgoDash = &Dashboard{
	Name:      "Gccgo",
	Namespace: "Gccgo",
	Prefix:    "/gccgo",
	Packages: []*Package{
		{
			Kind: "gccgo",
			Name: "Gccgo",
		},
	},
}

// hiddenBranches specifies branches that
// should not be displayed on the build dashboard.
// This also prevents the builder infrastructure
// from testing sub-repos against these branches.
var hiddenBranches = map[string]bool{
	"release-branch.go1.4":           true,
	"release-branch.go1.5":           true,
	"release-branch.go1.6":           true,
	"release-branch.go1.7":           true,
	"release-branch.go1.8":           true,
	"release-branch.go1.9":           true,
	"release-branch.go1.10-security": true,
	"release-branch.go1.11-security": true,
}
<commit_msg>app/appengine: remove Go 1.10 from the dashboard now that Go 1.12 is out<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"context"
	"net/http"
	"strings"

	"google.golang.org/appengine"
)

func handleFunc(path string, h http.HandlerFunc) {
	for _, d := range dashboards {
		http.Handle(d.Prefix+path, hstsHandler(h))
	}
}

// hstsHandler wraps an http.HandlerFunc such that it sets the HSTS header.
func hstsHandler(fn http.HandlerFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
		fn(w, r)
	})
}

// Dashboard describes a unique build dashboard.
type Dashboard struct {
	Name      string     // This dashboard's name (eg, "Go")
	Namespace string     // This dashboard's namespace (eg, "" (default), "Git")
	Prefix    string     // The path prefix (no trailing /)
	Packages  []*Package // The project's packages to build
}

// dashboardForRequest returns the appropriate dashboard for a given URL path.
func dashboardForRequest(r *http.Request) *Dashboard {
	for _, d := range dashboards[1:] {
		if d.Prefix == "" {
			panic("prefix can be empty only for the first dashboard")
		}
		if strings.HasPrefix(r.URL.Path, d.Prefix) {
			return d
		}
	}
	if dashboards[0].Prefix != "" {
		panic("prefix for the first dashboard should be empty")
	}
	return dashboards[0]
}

// Context returns a namespaced context for this dashboard, or panics if it
// fails to create a new context.
func (d *Dashboard) Context(c context.Context) context.Context {
	if d.Namespace == "" {
		return c
	}
	n, err := appengine.Namespace(c, d.Namespace)
	if err != nil {
		panic(err)
	}
	return n
}

// The currently known dashboards.
// The first one should have an empty prefix and
// the other ones a non empty prefix.
var dashboards = []*Dashboard{goDash, gccgoDash}

// goDash is the dashboard for the main go repository.
var goDash = &Dashboard{
	Name:      "Go",
	Namespace: "Git",
	Prefix:    "",
	Packages:  goPackages,
}

// goPackages is a list of all of the packages built by the main go repository.
var goPackages = []*Package{
	{
		Kind: "go",
		Name: "Go",
	},
	{
		Kind: "subrepo",
		Name: "arch",
		Path: "golang.org/x/arch",
	},
	{
		Kind: "subrepo",
		Name: "benchmarks",
		Path: "golang.org/x/benchmarks",
	},
	{
		Kind: "subrepo",
		Name: "blog",
		Path: "golang.org/x/blog",
	},
	{
		Kind: "subrepo",
		Name: "crypto",
		Path: "golang.org/x/crypto",
	},
	{
		Kind: "subrepo",
		Name: "debug",
		Path: "golang.org/x/debug",
	},
	{
		Kind: "subrepo",
		Name: "exp",
		Path: "golang.org/x/exp",
	},
	{
		Kind: "subrepo",
		Name: "image",
		Path: "golang.org/x/image",
	},
	{
		Kind: "subrepo",
		Name: "mobile",
		Path: "golang.org/x/mobile",
	},
	{
		Kind: "subrepo",
		Name: "net",
		Path: "golang.org/x/net",
	},
	{
		Kind: "subrepo",
		Name: "oauth2",
		Path: "golang.org/x/oauth2",
	},
	{
		Kind: "subrepo",
		Name: "perf",
		Path: "golang.org/x/perf",
	},
	{
		Kind: "subrepo",
		Name: "review",
		Path: "golang.org/x/review",
	},
	{
		Kind: "subrepo",
		Name: "sync",
		Path: "golang.org/x/sync",
	},
	{
		Kind: "subrepo",
		Name: "sys",
		Path: "golang.org/x/sys",
	},
	{
		Kind: "subrepo",
		Name: "talks",
		Path: "golang.org/x/talks",
	},
	{
		Kind: "subrepo",
		Name: "term",
		Path: "golang.org/x/term",
	},
	{
		Kind: "subrepo",
		Name: "text",
		Path: "golang.org/x/text",
	},
	{
		Kind: "subrepo",
		Name: "time",
		Path: "golang.org/x/time",
	},
	{
		Kind: "subrepo",
		Name: "tools",
		Path: "golang.org/x/tools",
	},
	{
		Kind: "subrepo",
		Name: "tour",
		Path: "golang.org/x/tour",
	},
}

// gccgoDash is the dashboard for gccgo.
var gccgoDash = &Dashboard{
	Name:      "Gccgo",
	Namespace: "Gccgo",
	Prefix:    "/gccgo",
	Packages: []*Package{
		{
			Kind: "gccgo",
			Name: "Gccgo",
		},
	},
}

// hiddenBranches specifies branches that
// should not be displayed on the build dashboard.
// This also prevents the builder infrastructure
// from testing sub-repos against these branches.
var hiddenBranches = map[string]bool{
	"release-branch.go1.4":           true,
	"release-branch.go1.5":           true,
	"release-branch.go1.6":           true,
	"release-branch.go1.7":           true,
	"release-branch.go1.8":           true,
	"release-branch.go1.9":           true,
	"release-branch.go1.10":          true,
	"release-branch.go1.10-security": true,
	"release-branch.go1.11-security": true,
}
<|endoftext|>