<commit_before>package webtorrent

import (
	"fmt"
	"log"
	"sync"

	"github.com/pion/datachannel"

	"github.com/pion/webrtc/v2"
)

var (
	api = func() *webrtc.API {
		// Enable the detach API (since it's non-standard but more idiomatic).
		s := webrtc.SettingEngine{}
		s.DetachDataChannels()
		return webrtc.NewAPI(webrtc.WithSettingEngine(s))
	}()
	config              = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
	newPeerConnectionMu sync.Mutex
)

func newPeerConnection() (*webrtc.PeerConnection, error) {
	newPeerConnectionMu.Lock()
	defer newPeerConnectionMu.Unlock()
	return api.NewPeerConnection(config)
}

type transport struct {
	pc *webrtc.PeerConnection
	dc *webrtc.DataChannel

	lock sync.Mutex
}

// newTransport creates a transport and returns a WebRTC offer to be announced
func newTransport() (*transport, webrtc.SessionDescription, error) {
	peerConnection, err := newPeerConnection()
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v\n", err)
	}
	dataChannel, err := peerConnection.CreateDataChannel("webrtc-datachannel", nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to data channel: %v\n", err)
	}
	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
	})

	dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
		fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data))
	})
	offer, err := peerConnection.CreateOffer(nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to create offer: %v\n", err)
	}
	err = peerConnection.SetLocalDescription(offer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %v\n", err)
	}

	t := &transport{pc: peerConnection, dc: dataChannel}
	return t, offer, nil
}

// newTransportFromOffer creates a transport from a WebRTC offer and and returns a WebRTC answer to
// be announced.
func newTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannelOpen, offerId string) (*transport, webrtc.SessionDescription, error) {
	peerConnection, err := newPeerConnection()
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v", err)
	}
	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
	})

	t := &transport{pc: peerConnection}

	err = peerConnection.SetRemoteDescription(offer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
	}
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
	}
	peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
		fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
		t.lock.Lock()
		t.dc = d
		t.lock.Unlock()
		t.handleOpen(func(dc datachannel.ReadWriteCloser) {
			onOpen(dc, DataChannelContext{answer, offer, offerId, false})
		})
	})
	err = peerConnection.SetLocalDescription(answer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
	}

	return t, answer, nil
}

// SetAnswer sets the WebRTC answer
func (t *transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
	t.handleOpen(onOpen)

	err := t.pc.SetRemoteDescription(answer)
	if err != nil {
		return err
	}
	return nil
}

func (t *transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
	t.lock.Lock()
	dc := t.dc
	t.lock.Unlock()
	dc.OnOpen(func() {
		fmt.Printf("Data channel '%s'-'%d' open.\n", dc.Label(), dc.ID())

		// Detach the data channel
		raw, err := dc.Detach()
		if err != nil {
			log.Fatalf("failed to detach: %v", err) // TODO: Error handling
		}

		onOpen(raw)
	})
}
<commit_msg>More error handling and logging clean up<commit_after>package webtorrent

import (
	"fmt"
	"log"
	"sync"

	"github.com/pion/datachannel"

	"github.com/pion/webrtc/v2"
)

var (
	api = func() *webrtc.API {
		// Enable the detach API (since it's non-standard but more idiomatic).
		s := webrtc.SettingEngine{}
		s.DetachDataChannels()
		return webrtc.NewAPI(webrtc.WithSettingEngine(s))
	}()
	config              = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
	newPeerConnectionMu sync.Mutex
)

func newPeerConnection() (*webrtc.PeerConnection, error) {
	newPeerConnectionMu.Lock()
	defer newPeerConnectionMu.Unlock()
	return api.NewPeerConnection(config)
}

type transport struct {
	pc *webrtc.PeerConnection
	dc *webrtc.DataChannel

	lock sync.Mutex
}

// newTransport creates a transport and returns a WebRTC offer to be announced
func newTransport() (*transport, webrtc.SessionDescription, error) {
	peerConnection, err := newPeerConnection()
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %w", err)
	}
	dataChannel, err := peerConnection.CreateDataChannel("webrtc-datachannel", nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to data channel: %w", err)
	}
	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		//fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
	})

	dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
		//fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data))
	})
	offer, err := peerConnection.CreateOffer(nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to create offer: %w", err)
	}
	err = peerConnection.SetLocalDescription(offer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %w", err)
	}

	t := &transport{pc: peerConnection, dc: dataChannel}
	return t, offer, nil
}

// newTransportFromOffer creates a transport from a WebRTC offer and and returns a WebRTC answer to
// be announced.
func newTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannelOpen, offerId string) (*transport, webrtc.SessionDescription, error) {
	peerConnection, err := newPeerConnection()
	if err != nil {
		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %w", err)
	}
	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		//fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
	})

	t := &transport{pc: peerConnection}

	err = peerConnection.SetRemoteDescription(offer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, err
	}
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		return nil, webrtc.SessionDescription{}, err
	}
	peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
		//fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
		t.lock.Lock()
		t.dc = d
		t.lock.Unlock()
		t.handleOpen(func(dc datachannel.ReadWriteCloser) {
			onOpen(dc, DataChannelContext{answer, offer, offerId, false})
		})
	})
	err = peerConnection.SetLocalDescription(answer)
	if err != nil {
		return nil, webrtc.SessionDescription{}, err
	}

	return t, answer, nil
}

// SetAnswer sets the WebRTC answer
func (t *transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
	t.handleOpen(onOpen)

	err := t.pc.SetRemoteDescription(answer)
	if err != nil {
		return err
	}
	return nil
}

func (t *transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
	t.lock.Lock()
	dc := t.dc
	t.lock.Unlock()
	dc.OnOpen(func() {
		//fmt.Printf("Data channel '%s'-'%d' open.\n", dc.Label(), dc.ID())

		// Detach the data channel
		raw, err := dc.Detach()
		if err != nil {
			log.Fatalf("failed to detach: %v", err) // TODO: Error handling
		}

		onOpen(raw)
	})
}
<|endoftext|>


<commit_before>/*
Go micro provides a pluggable library to build microservices.

	import (
		micro "github.com/micro/go-micro"
	)

	service := micro.NewService()
	h := service.Server().NewHandler(&Greeter{})
	service.Server().Handle(h)
	service.Run()

	req := service.Client().NewRequest(service, method, request)
	rsp := response{}
	err := service.Client().Call(req, rsp)

*/

package gomicro

import (
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/server"
)

type Service interface {
	Client() client.Client
	Server() server.Server
	Run() error
	String() string
}

type Option func(*Options)

var (
	HeaderPrefix   = "X-Micro-"
	DefaultService = newService()
)

func NewService(opts ...Option) Service {
	return newService(opts...)
}
<commit_msg>Remove default service<commit_after>/*
Go micro provides a pluggable library to build microservices.

	import (
		micro "github.com/micro/go-micro"
	)

	service := micro.NewService()
	h := service.Server().NewHandler(&Greeter{})
	service.Server().Handle(h)
	service.Run()

	req := service.Client().NewRequest(service, method, request)
	rsp := response{}
	err := service.Client().Call(req, rsp)

*/

package gomicro

import (
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/server"
)

type Service interface {
	Client() client.Client
	Server() server.Server
	Run() error
	String() string
}

type Option func(*Options)

var (
	HeaderPrefix = "X-Micro-"
)

func NewService(opts ...Option) Service {
	return newService(opts...)
}
<|endoftext|>


<commit_before>package libct

// #cgo CFLAGS: -DCONFIG_X86_64 -DARCH="x86" -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
// #cgo LDFLAGS: -l:libct.a -l:libnl-route-3.a -l:libnl-3.a -l:libapparmor.a -l:libselinux.a -lm
// #include "../src/include/uapi/libct.h"
// #include "../src/include/uapi/libct-errors.h"
import "C"
import "fmt"
import "unsafe"

const (
	LIBCT_OPT_AUTO_PROC_MOUNT = C.LIBCT_OPT_AUTO_PROC_MOUNT
	CAPS_BSET                 = C.CAPS_BSET
	CAPS_ALLCAPS              = C.CAPS_ALLCAPS
	CAPS_ALL                  = C.CAPS_ALL
)

type file interface {
	Fd() uintptr
	Close() error
	Read(p []byte) (n int, err error)
	Write(p []byte) (n int, err error)
}

type console struct {
}

var Console console

func (c console) Fd() uintptr {
	return ^uintptr(0)
}

func (c console) Close() error {
	return nil
}

func (c console) Read(p []byte) (n int, err error) {
	return 0, nil
}

func (c console) Write(p []byte) (n int, err error) {
	return 0, nil
}

type Session struct {
	s C.libct_session_t
}

type Container struct {
	ct C.ct_handler_t
}

type NetDev struct {
	dev C.ct_net_t
}

type NetRoute struct {
	route C.ct_net_route_t
}

type NetRouteNextHop struct {
	nh C.ct_net_route_nh_t
}

type LibctError struct {
	Code int
}

func (e LibctError) Error() string {
	return fmt.Sprintf("LibctError: %x", e.Code)
}

func (s *Session) OpenLocal() error {
	h := C.libct_session_open_local()

	if C.libct_handle_is_err(unsafe.Pointer(h)) != 0 {
		return LibctError{int(C.libct_handle_to_err(unsafe.Pointer(h)))}
	}

	s.s = h

	return nil
}

func (s *Session) ContainerCreate(name string) (*Container, error) {
	ct := C.libct_container_create(s.s, C.CString(name))

	if C.libct_handle_is_err(unsafe.Pointer(ct)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(ct)))}
	}

	return &Container{ct}, nil
}

func (s *Session) ContainerOpen(name string) (*Container, error) {
	ct := C.libct_container_open(s.s, C.CString(name))

	if C.libct_handle_is_err(unsafe.Pointer(ct)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(ct)))}
	}

	return &Container{ct}, nil
}

func (s *Session) ProcessCreateDesc() (*ProcessDesc, error) {
	p := C.libct_process_desc_create(s.s)
	if C.libct_handle_is_err(unsafe.Pointer(p)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(p)))}
	}

	return &ProcessDesc{desc: p}, nil
}

func (ct *Container) SetNsMask(nsmask uint64) error {
	ret := C.libct_container_set_nsmask(ct.ct, C.ulong(nsmask))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) Kill() error {
	ret := C.libct_container_kill(ct.ct)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func getFd(f file) C.int {
	if _, ok := f.(console); ok {
		return C.LIBCT_CONSOLE_FD
	}

	return C.int(f.Fd())
}

func (ct *Container) SetConsoleFd(f file) error {
	ret := C.libct_container_set_console_fd(ct.ct, getFd(f))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SpawnExecve(p *ProcessDesc, path string, argv []string, env []string) (error) {
	err := ct.execve(p, path, argv, env, true)

	return err
}

func (ct *Container) EnterExecve(p *ProcessDesc, path string, argv []string, env []string) (error) {
	err := ct.execve(p, path, argv, env, false)
	return err
}

func (ct *Container) execve(p *ProcessDesc, path string, argv []string, env []string, spawn bool) (error) {
	var (
		h     C.ct_process_t
		i   int = 0
	)

	type F func(*ProcessDesc) (file, error)
	for _, setupFd := range []F{(*ProcessDesc).stdin, (*ProcessDesc).stdout, (*ProcessDesc).stderr} {
		fd, err := setupFd(p)
		if err != nil {
			p.closeDescriptors(p.closeAfterStart)
			p.closeDescriptors(p.closeAfterWait)
			return err
		}
		p.childFiles = append(p.childFiles, fd)
		i = i + 1
	}

	p.childFiles = append(p.childFiles, p.ExtraFiles...)

	cargv := make([]*C.char, len(argv)+1)
	for i, arg := range argv {
		cargv[i] = C.CString(arg)
	}

	cenv := make([]*C.char, len(env)+1)
	for i, e := range env {
		cenv[i] = C.CString(e)
	}

	cfds := make([]C.int, len(p.childFiles))
	for i, fd := range p.childFiles {
		cfds[i] = C.int(getFd(fd))
	}

	C.libct_process_desc_set_fds(p.desc, &cfds[0], C.int(len(p.childFiles)))

	if spawn {
		h = C.libct_container_spawn_execve(ct.ct, p.desc, C.CString(path), &cargv[0], &cenv[0])
	} else {
		h = C.libct_container_enter_execve(ct.ct, p.desc, C.CString(path), &cargv[0], &cenv[0])
	}

	if C.libct_handle_is_err(unsafe.Pointer(h)) != 0 {
		p.closeDescriptors(p.closeAfterStart)
		p.closeDescriptors(p.closeAfterWait)
		return  LibctError{int(C.libct_handle_to_err(unsafe.Pointer(h)))}
	}

	p.closeDescriptors(p.closeAfterStart)

	p.errch = make(chan error, len(p.goroutine))
	for _, fn := range p.goroutine {
		go func(fn func() error) {
			p.errch <- fn()
		}(fn)
	}

	p.handle = h

	return nil
}

func (ct *Container) Wait() error {
	ret := C.libct_container_wait(ct.ct)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) Uname(host *string, domain *string) error {
	var chost *C.char
	var cdomain *C.char

	if host != nil {
		chost = C.CString(*host)
	}

	if domain != nil {
		cdomain = C.CString(*domain)
	}

	ret := C.libct_container_uname(ct.ct, chost, cdomain)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SetRoot(root string) error {

	if ret := C.libct_fs_set_root(ct.ct, C.CString(root)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

const (
	CT_FS_RDONLY      = C.CT_FS_RDONLY
	CT_FS_PRIVATE     = C.CT_FS_PRIVATE
	CT_FS_NOEXEC      = C.CT_FS_NOEXEC
	CT_FS_NOSUID      = C.CT_FS_NOSUID
	CT_FS_NODEV       = C.CT_FS_NODEV
	CT_FS_STRICTATIME = C.CT_FS_STRICTATIME
)

func (ct *Container) AddBindMount(src string, dst string, flags int) error {

	if ret := C.libct_fs_add_bind_mount(ct.ct, C.CString(src), C.CString(dst), C.int(flags)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) AddMount(src string, dst string, flags int, fstype string, data string) error {

	if ret := C.libct_fs_add_mount(ct.ct, C.CString(src), C.CString(dst), C.int(flags), C.CString(fstype), C.CString(data)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SetOption(opt int32) error {
	if ret := C.libct_container_set_option(ct.ct, C.int(opt), nil); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) AddDeviceNode(path string, mode int, major int, minor int) error {

	ret := C.libct_fs_add_devnode(ct.ct, C.CString(path), C.int(mode), C.int(major), C.int(minor))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (nd *NetDev) GetPeer() (*NetDev, error) {

	dev := C.libct_net_dev_get_peer(nd.dev)

	if C.libct_handle_is_err(unsafe.Pointer(dev)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(dev)))}
	}

	return &NetDev{dev}, nil
}

func (ct *Container) AddNetVeth(host_name string, ct_name string) (*NetDev, error) {

	var args C.struct_ct_net_veth_arg

	args.host_name = C.CString(host_name)
	args.ct_name = C.CString(ct_name)

	dev := C.libct_net_add(ct.ct, C.CT_NET_VETH, unsafe.Pointer(&args))

	if C.libct_handle_is_err(unsafe.Pointer(dev)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(dev)))}
	}

	return &NetDev{dev}, nil
}

func (dev *NetDev) AddIpAddr(addr string) error {
	err := C.libct_net_dev_add_ip_addr(dev.dev, C.CString(addr))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (dev *NetDev) SetMaster(master string) error {
	err := C.libct_net_dev_set_master(dev.dev, C.CString(master))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (dev *NetDev) SetMtu(mtu int) error {
	err := C.libct_net_dev_set_mtu(dev.dev, C.int(mtu))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (ct *Container) AddRoute() (*NetRoute, error) {
	r := C.libct_net_route_add(ct.ct)

	if C.libct_handle_is_err(unsafe.Pointer(r)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(r)))}
	}

	return &NetRoute{r}, nil
}

func (route *NetRoute) SetSrc(src string) error {
	err := C.libct_net_route_set_src(route.route, C.CString(src))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) SetDst(dst string) error {
	err := C.libct_net_route_set_dst(route.route, C.CString(dst))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) SetDev(dev string) error {
	err := C.libct_net_route_set_dev(route.route, C.CString(dev))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) AddNextHop() (*NetRouteNextHop, error) {
	nh := C.libct_net_route_add_nh(route.route)
	if C.libct_handle_is_err(unsafe.Pointer(nh)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(nh)))}
	}

	return &NetRouteNextHop{nh}, nil
}

func (nh *NetRouteNextHop) SetGateway(addr string) error {
	err := C.libct_net_route_nh_set_gw(nh.nh, C.CString(addr))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (nh *NetRouteNextHop) SetDev(dev string) error {
	err := C.libct_net_route_nh_set_dev(nh.nh, C.CString(dev))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}
<commit_msg>go: add ct.AddController()<commit_after>package libct

// #cgo CFLAGS: -DCONFIG_X86_64 -DARCH="x86" -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
// #cgo LDFLAGS: -l:libct.a -l:libnl-route-3.a -l:libnl-3.a -l:libapparmor.a -l:libselinux.a -lm
// #include "../src/include/uapi/libct.h"
// #include "../src/include/uapi/libct-errors.h"
import "C"
import "fmt"
import "unsafe"

const (
	LIBCT_OPT_AUTO_PROC_MOUNT = C.LIBCT_OPT_AUTO_PROC_MOUNT
	CAPS_BSET                 = C.CAPS_BSET
	CAPS_ALLCAPS              = C.CAPS_ALLCAPS
	CAPS_ALL                  = C.CAPS_ALL
)

type file interface {
	Fd() uintptr
	Close() error
	Read(p []byte) (n int, err error)
	Write(p []byte) (n int, err error)
}

type console struct {
}

var Console console

func (c console) Fd() uintptr {
	return ^uintptr(0)
}

func (c console) Close() error {
	return nil
}

func (c console) Read(p []byte) (n int, err error) {
	return 0, nil
}

func (c console) Write(p []byte) (n int, err error) {
	return 0, nil
}

type Session struct {
	s C.libct_session_t
}

type Container struct {
	ct C.ct_handler_t
}

type NetDev struct {
	dev C.ct_net_t
}

type NetRoute struct {
	route C.ct_net_route_t
}

type NetRouteNextHop struct {
	nh C.ct_net_route_nh_t
}

type LibctError struct {
	Code int
}

func (e LibctError) Error() string {
	return fmt.Sprintf("LibctError: %x", e.Code)
}

func (s *Session) OpenLocal() error {
	h := C.libct_session_open_local()

	if C.libct_handle_is_err(unsafe.Pointer(h)) != 0 {
		return LibctError{int(C.libct_handle_to_err(unsafe.Pointer(h)))}
	}

	s.s = h

	return nil
}

func (s *Session) ContainerCreate(name string) (*Container, error) {
	ct := C.libct_container_create(s.s, C.CString(name))

	if C.libct_handle_is_err(unsafe.Pointer(ct)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(ct)))}
	}

	return &Container{ct}, nil
}

func (s *Session) ContainerOpen(name string) (*Container, error) {
	ct := C.libct_container_open(s.s, C.CString(name))

	if C.libct_handle_is_err(unsafe.Pointer(ct)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(ct)))}
	}

	return &Container{ct}, nil
}

func (s *Session) ProcessCreateDesc() (*ProcessDesc, error) {
	p := C.libct_process_desc_create(s.s)
	if C.libct_handle_is_err(unsafe.Pointer(p)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(p)))}
	}

	return &ProcessDesc{desc: p}, nil
}

func (ct *Container) SetNsMask(nsmask uint64) error {
	ret := C.libct_container_set_nsmask(ct.ct, C.ulong(nsmask))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) Kill() error {
	ret := C.libct_container_kill(ct.ct)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func getFd(f file) C.int {
	if _, ok := f.(console); ok {
		return C.LIBCT_CONSOLE_FD
	}

	return C.int(f.Fd())
}

func (ct *Container) SetConsoleFd(f file) error {
	ret := C.libct_container_set_console_fd(ct.ct, getFd(f))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SpawnExecve(p *ProcessDesc, path string, argv []string, env []string) (error) {
	err := ct.execve(p, path, argv, env, true)

	return err
}

func (ct *Container) EnterExecve(p *ProcessDesc, path string, argv []string, env []string) (error) {
	err := ct.execve(p, path, argv, env, false)
	return err
}

func (ct *Container) execve(p *ProcessDesc, path string, argv []string, env []string, spawn bool) (error) {
	var (
		h     C.ct_process_t
		i   int = 0
	)

	type F func(*ProcessDesc) (file, error)
	for _, setupFd := range []F{(*ProcessDesc).stdin, (*ProcessDesc).stdout, (*ProcessDesc).stderr} {
		fd, err := setupFd(p)
		if err != nil {
			p.closeDescriptors(p.closeAfterStart)
			p.closeDescriptors(p.closeAfterWait)
			return err
		}
		p.childFiles = append(p.childFiles, fd)
		i = i + 1
	}

	p.childFiles = append(p.childFiles, p.ExtraFiles...)

	cargv := make([]*C.char, len(argv)+1)
	for i, arg := range argv {
		cargv[i] = C.CString(arg)
	}

	cenv := make([]*C.char, len(env)+1)
	for i, e := range env {
		cenv[i] = C.CString(e)
	}

	cfds := make([]C.int, len(p.childFiles))
	for i, fd := range p.childFiles {
		cfds[i] = C.int(getFd(fd))
	}

	C.libct_process_desc_set_fds(p.desc, &cfds[0], C.int(len(p.childFiles)))

	if spawn {
		h = C.libct_container_spawn_execve(ct.ct, p.desc, C.CString(path), &cargv[0], &cenv[0])
	} else {
		h = C.libct_container_enter_execve(ct.ct, p.desc, C.CString(path), &cargv[0], &cenv[0])
	}

	if C.libct_handle_is_err(unsafe.Pointer(h)) != 0 {
		p.closeDescriptors(p.closeAfterStart)
		p.closeDescriptors(p.closeAfterWait)
		return  LibctError{int(C.libct_handle_to_err(unsafe.Pointer(h)))}
	}

	p.closeDescriptors(p.closeAfterStart)

	p.errch = make(chan error, len(p.goroutine))
	for _, fn := range p.goroutine {
		go func(fn func() error) {
			p.errch <- fn()
		}(fn)
	}

	p.handle = h

	return nil
}

func (ct *Container) Wait() error {
	ret := C.libct_container_wait(ct.ct)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) Uname(host *string, domain *string) error {
	var chost *C.char
	var cdomain *C.char

	if host != nil {
		chost = C.CString(*host)
	}

	if domain != nil {
		cdomain = C.CString(*domain)
	}

	ret := C.libct_container_uname(ct.ct, chost, cdomain)

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SetRoot(root string) error {

	if ret := C.libct_fs_set_root(ct.ct, C.CString(root)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

const (
	CT_FS_RDONLY      = C.CT_FS_RDONLY
	CT_FS_PRIVATE     = C.CT_FS_PRIVATE
	CT_FS_NOEXEC      = C.CT_FS_NOEXEC
	CT_FS_NOSUID      = C.CT_FS_NOSUID
	CT_FS_NODEV       = C.CT_FS_NODEV
	CT_FS_STRICTATIME = C.CT_FS_STRICTATIME
)

func (ct *Container) AddBindMount(src string, dst string, flags int) error {

	if ret := C.libct_fs_add_bind_mount(ct.ct, C.CString(src), C.CString(dst), C.int(flags)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) AddMount(src string, dst string, flags int, fstype string, data string) error {

	if ret := C.libct_fs_add_mount(ct.ct, C.CString(src), C.CString(dst), C.int(flags), C.CString(fstype), C.CString(data)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

const (
	CTL_BLKIO	= C.CTL_BLKIO
	CTL_CPU		= C.CTL_CPU
	CTL_CPUACCT	= C.CTL_CPUACCT
	CTL_CPUSET	= C.CTL_CPUSET
	CTL_DEVICES	= C.CTL_DEVICES
	CTL_FREEZER	= C.CTL_FREEZER
	CTL_HUGETLB	= C.CTL_HUGETLB
	CTL_MEMORY	= C.CTL_MEMORY
	CTL_NETCLS	= C.CTL_NETCLS
)

func (ct *Container) AddController(ctype int) error {
	if ret := C.libct_controller_add(ct.ct, C.enum_ct_controller(ctype)); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) SetOption(opt int32) error {
	if ret := C.libct_container_set_option(ct.ct, C.int(opt), nil); ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (ct *Container) AddDeviceNode(path string, mode int, major int, minor int) error {

	ret := C.libct_fs_add_devnode(ct.ct, C.CString(path), C.int(mode), C.int(major), C.int(minor))

	if ret != 0 {
		return LibctError{int(ret)}
	}

	return nil
}

func (nd *NetDev) GetPeer() (*NetDev, error) {

	dev := C.libct_net_dev_get_peer(nd.dev)

	if C.libct_handle_is_err(unsafe.Pointer(dev)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(dev)))}
	}

	return &NetDev{dev}, nil
}

func (ct *Container) AddNetVeth(host_name string, ct_name string) (*NetDev, error) {

	var args C.struct_ct_net_veth_arg

	args.host_name = C.CString(host_name)
	args.ct_name = C.CString(ct_name)

	dev := C.libct_net_add(ct.ct, C.CT_NET_VETH, unsafe.Pointer(&args))

	if C.libct_handle_is_err(unsafe.Pointer(dev)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(dev)))}
	}

	return &NetDev{dev}, nil
}

func (dev *NetDev) AddIpAddr(addr string) error {
	err := C.libct_net_dev_add_ip_addr(dev.dev, C.CString(addr))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (dev *NetDev) SetMaster(master string) error {
	err := C.libct_net_dev_set_master(dev.dev, C.CString(master))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (dev *NetDev) SetMtu(mtu int) error {
	err := C.libct_net_dev_set_mtu(dev.dev, C.int(mtu))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (ct *Container) AddRoute() (*NetRoute, error) {
	r := C.libct_net_route_add(ct.ct)

	if C.libct_handle_is_err(unsafe.Pointer(r)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(r)))}
	}

	return &NetRoute{r}, nil
}

func (route *NetRoute) SetSrc(src string) error {
	err := C.libct_net_route_set_src(route.route, C.CString(src))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) SetDst(dst string) error {
	err := C.libct_net_route_set_dst(route.route, C.CString(dst))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) SetDev(dev string) error {
	err := C.libct_net_route_set_dev(route.route, C.CString(dev))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (route *NetRoute) AddNextHop() (*NetRouteNextHop, error) {
	nh := C.libct_net_route_add_nh(route.route)
	if C.libct_handle_is_err(unsafe.Pointer(nh)) != 0 {
		return nil, LibctError{int(C.libct_handle_to_err(unsafe.Pointer(nh)))}
	}

	return &NetRouteNextHop{nh}, nil
}

func (nh *NetRouteNextHop) SetGateway(addr string) error {
	err := C.libct_net_route_nh_set_gw(nh.nh, C.CString(addr))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}

func (nh *NetRouteNextHop) SetDev(dev string) error {
	err := C.libct_net_route_nh_set_dev(nh.nh, C.CString(dev))
	if err != 0 {
		return LibctError{int(err)}
	}

	return nil
}
<|endoftext|>


<commit_before>// simplistic http sessions based on memcache, with in-memory stub for development
package gomemssn

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/gob"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"log"
	"net/http"
	"sync"
	"time"
)

// NewManager returns a new *Manager with sensible defaults.
// You need to provide the memcache client and
// an optional prefix for the keys we store in memcache.
func NewManager(client *memcache.Client, keyPrefix string) *Manager {

	if client == nil {
		log.Printf("NOTE: Memcache client is nil, falling back to storing sessions in memory with no expiration! This should only occur in a development environment, not in production.")
	}

	return &Manager{
		Expiration:        time.Minute * 30,
		TemplateCookie:    &http.Cookie{Name: keyPrefix + "_gomemssn", Path: "/", MaxAge: 60 * 30},
		MemcacheKeyPrefix: keyPrefix,
		Client:            client,
		stubClient:        make(map[string]*Session),
	}

}

func newKey() string {
	b := make([]byte, 33)
	rand.Read(b)
	return base64.URLEncoding.EncodeToString(b)
}

type Manager struct {
	TemplateCookie    *http.Cookie        // this cookie is copied and the value modified for each one written to the client
	Expiration        time.Duration       // how long until session expiration - passed back to memcache
	Client            *memcache.Client    // the memcache client or nil to mean store in memory (stub for development)
	MemcacheKeyPrefix string              // prefix memcache keys with this
	stubClient        map[string]*Session // if client is null then we store sessions in memory here
	stubClientMutex   sync.RWMutex        // control access to stubClient
}

type Session struct {
	Key    string       // the key for this session
	Cookie *http.Cookie // the cookie we will write to the client
	Values Values       // values of the session
}

type Values map[string]interface {
}

// TODO: make a way to delete a session and recreate it with a new id - to prevent
// session fixation attacks.  You would call this function after logging in or
// other access escalation, to avoid someone else piggy backing on your session.

// Get or create the session object, sets the appropriate cookie, does
// not write to the backing store
func (m *Manager) Session(w http.ResponseWriter, r *http.Request) (ret *Session, err error) {

	name := m.TemplateCookie.Name
	if name == "" {
		return nil, fmt.Errorf("TemplateCookie cannot have empty string as name - put something in there")
	}

	cookie, err := r.Cookie(name)
	if err == nil && len(cookie.Value) > 0 {

		key := cookie.Value

		if m.Client != nil {

			it, err := m.Client.Get(key)
			if err == memcache.ErrCacheMiss {
				ret = &Session{Key: key, Values: make(Values)}
			} else if err != nil {
				return nil, err
			} else {
				ret = &Session{Key: key, Values: make(Values)}
				err = gob.NewDecoder(bytes.NewReader(it.Value)).Decode(&ret.Values)
				if err != nil {
					return nil, err
				}
			}

		} else {
			// look up the stub session
			m.stubClientMutex.RLock()
			ret = m.stubClient[key]
			m.stubClientMutex.RUnlock()
			if ret == nil {
				ret = &Session{Key: newKey(), Values: make(Values)}
			}
		}

	} else {
		// new empty session
		ret = &Session{Key: newKey(), Values: make(Values)}
	}

	// copy the cookie
	newc := *m.TemplateCookie
	// newc.MaxAge = int(m.Expiration / time.Second)
	newc.Value = ret.Key
	ret.Cookie = &newc

	// set it on the response writer - so the key goes back to the client
	http.SetCookie(w, ret.Cookie)

	return ret, nil

}

func (m *Manager) MustSession(w http.ResponseWriter, r *http.Request) *Session {
	ret, err := m.Session(w, r)
	if err != nil {
		panic(err)
	}
	return ret
}

// write the actual session back to he memcache backend
func (m *Manager) WriteSession(w http.ResponseWriter, s *Session) error {

	key := s.Key

	if m.Client == nil {
		m.stubClientMutex.Lock()
		m.stubClient[key] = s
		m.stubClientMutex.Unlock()
	} else {

		buf := &bytes.Buffer{}
		enc := gob.NewEncoder(buf)
		err := enc.Encode(s.Values)
		if err != nil {
			return err
		}
		exp := int32(m.Expiration / time.Second)
		err = m.Client.Set(&memcache.Item{Key: key, Value: buf.Bytes(), Expiration: exp})
		if err != nil {
			return err
		}

	}

	return nil

}

func (m *Manager) MustWriteSession(w http.ResponseWriter, s *Session) {
	err := m.WriteSession(w, s)
	if err != nil {
		panic(err)
	}
}
<commit_msg>added helper stuff on session and values<commit_after>// simplistic http sessions based on memcache, with in-memory stub for development
package gomemssn

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/gob"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"log"
	"net/http"
	"sync"
	"time"
)

// NewManager returns a new *Manager with sensible defaults.
// You need to provide the memcache client and
// an optional prefix for the keys we store in memcache.
func NewManager(client *memcache.Client, keyPrefix string) *Manager {

	if client == nil {
		log.Printf("NOTE: Memcache client is nil, falling back to storing sessions in memory with no expiration! This should only occur in a development environment, not in production.")
	}

	return &Manager{
		Expiration:        time.Minute * 30,
		TemplateCookie:    &http.Cookie{Name: keyPrefix + "_gomemssn", Path: "/", MaxAge: 60 * 30},
		MemcacheKeyPrefix: keyPrefix,
		Client:            client,
		stubClient:        make(map[string]*Session),
	}

}

func newKey() string {
	b := make([]byte, 33)
	rand.Read(b)
	return base64.URLEncoding.EncodeToString(b)
}

type Manager struct {
	TemplateCookie    *http.Cookie        // this cookie is copied and the value modified for each one written to the client
	Expiration        time.Duration       // how long until session expiration - passed back to memcache
	Client            *memcache.Client    // the memcache client or nil to mean store in memory (stub for development)
	MemcacheKeyPrefix string              // prefix memcache keys with this
	stubClient        map[string]*Session // if client is null then we store sessions in memory here
	stubClientMutex   sync.RWMutex        // control access to stubClient
}

type Session struct {
	Key    string       // the key for this session
	Cookie *http.Cookie // the cookie we will write to the client
	Values Values       // values of the session
}

// convenience function to add a "flash message" to this session - uses the key "_flashes"
func (s *Session) AddFlash(v interface{}) {
	flashes := []interface{}{}
	// extract existing flash messages
	f := s.Values["_flashes"]
	if f != nil {
		if f1, ok := f.([]interface{}); ok {
			flashes = f1
		}
	}
	// append this one
	flashes = append(flashes, v)
	// set it back
	s.Values["_flashes"] = flashes
}

// pops the "flash messages" from this session
func (s *Session) Flashes() []interface{} {
	f := s.Values["_flashes"]
	if f != nil {
		if f1, ok := f.([]interface{}); ok {
			delete(s.Values, "_flashes")
			return f1
		}
	}
	return nil
}

type Values map[string]interface{}

func (v Values) GetString(key string) string {
	val, ok := v[key]
	if !ok {
		return ""
	}
	ret, ok := val.(string)
	if !ok {
		return ""
	}
	return ret
}
func (v Values) SetString(key string, val string) {
	v[key] = val
}
func (v Values) GetInt64(key string) int64 {
	val, ok := v[key]
	if !ok {
		return 0
	}
	ret, ok := val.(int64)
	if !ok {
		return 0
	}
	return ret
}
func (v Values) SetInt64(key string, val int64) {
	v[key] = val
}
func (v Values) GetFloat64(key string) float64 {
	val, ok := v[key]
	if !ok {
		return 0
	}
	ret, ok := val.(float64)
	if !ok {
		return 0
	}
	return ret
}
func (v Values) SetFloat64(key string, val float64) {
	v[key] = val
}
func (v Values) GetBool(key string) bool {
	val, ok := v[key]
	if !ok {
		return false
	}
	ret, ok := val.(bool)
	if !ok {
		return false
	}
	return ret
}
func (v Values) SetBool(key string, val bool) {
	v[key] = val
}

// TODO: make a way to delete a session and recreate it with a new id - to prevent
// session fixation attacks.  You would call this function after logging in or
// other access escalation, to avoid someone else piggy backing on your session.

// Get or create the session object, sets the appropriate cookie, does
// not write to the backing store
func (m *Manager) Session(w http.ResponseWriter, r *http.Request) (ret *Session, err error) {

	name := m.TemplateCookie.Name
	if name == "" {
		return nil, fmt.Errorf("TemplateCookie cannot have empty string as name - put something in there")
	}

	cookie, err := r.Cookie(name)
	if err == nil && len(cookie.Value) > 0 {

		key := cookie.Value

		if m.Client != nil {

			it, err := m.Client.Get(key)
			if err == memcache.ErrCacheMiss {
				ret = &Session{Key: key, Values: make(Values)}
			} else if err != nil {
				return nil, err
			} else {
				ret = &Session{Key: key, Values: make(Values)}
				err = gob.NewDecoder(bytes.NewReader(it.Value)).Decode(&ret.Values)
				if err != nil {
					return nil, err
				}
			}

		} else {
			// look up the stub session
			m.stubClientMutex.RLock()
			ret = m.stubClient[key]
			m.stubClientMutex.RUnlock()
			if ret == nil {
				ret = &Session{Key: newKey(), Values: make(Values)}
			}
		}

	} else {
		// new empty session
		ret = &Session{Key: newKey(), Values: make(Values)}
	}

	// copy the cookie
	newc := *m.TemplateCookie
	// newc.MaxAge = int(m.Expiration / time.Second)
	newc.Value = ret.Key
	ret.Cookie = &newc

	// set it on the response writer - so the key goes back to the client
	http.SetCookie(w, ret.Cookie)

	return ret, nil

}

func (m *Manager) MustSession(w http.ResponseWriter, r *http.Request) *Session {
	ret, err := m.Session(w, r)
	if err != nil {
		panic(err)
	}
	return ret
}

// write the actual session back to he memcache backend
func (m *Manager) WriteSession(w http.ResponseWriter, s *Session) error {

	key := s.Key

	if m.Client == nil {
		m.stubClientMutex.Lock()
		m.stubClient[key] = s
		m.stubClientMutex.Unlock()
	} else {

		buf := &bytes.Buffer{}
		enc := gob.NewEncoder(buf)
		err := enc.Encode(s.Values)
		if err != nil {
			return err
		}
		exp := int32(m.Expiration / time.Second)
		err = m.Client.Set(&memcache.Item{Key: key, Value: buf.Bytes(), Expiration: exp})
		if err != nil {
			return err
		}

	}

	return nil

}

func (m *Manager) MustWriteSession(w http.ResponseWriter, s *Session) {
	err := m.WriteSession(w, s)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Usage: (uh, eventually, once all of this is implemented...)
// $ googlecl help  # prints help
// $ googlecl list  # lists all available APIs
// $ googlecl describe calendar                          # describes all methods available in Calendar API
// $ googlecl describe calendar calendars.get            # describes one method in Calendar API
//
// $ googlecl calendar calendars.get --calendarId=12345  # prints JSON API response
//
// $ cat someEvent.json | googlecl calendar events.insert --calendarId=12345 --in  # inserts an event
// $ googlecl calendar events.insert --calendarId=12345 --inFile=someEvent.json    # equivalent to above
//
// TODO: Handle auth somehow.
// TODO: Cache discovery/directory documents for faster requests.
// TODO: Handle media upload/download.

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

var cmds = map[string]func(){
	"help":     func() { log.Fatal("TODO: implement help command") },
	"list":     func() { log.Fatal("TODO: implement list command") },
	"describe": func() { log.Fatal("TODO: implement describe command") },
}

func parseArgs(args []string) map[string]string {
	m := make(map[string]string)
	for _, a := range args {
		if strings.HasPrefix(a, "--") {
			a = a[2:]
		} else if strings.HasPrefix(a, "-") {
			a = a[1:]
		} else {
			log.Fatalf("Invalid flag format %s", a)
		}

		if !strings.Contains(a, "=") {
			m[a] = "true"
		} else {
			parts := strings.SplitN(a, "=", 2)
			m[parts[0]] = parts[1]
		}
	}
	return m
}

func main() {
	if len(os.Args) == 1 {
		cmds["help"]()
		return
	}

	cmd := os.Args[1]
	if cmd == "" {
		log.Fatal("Must specify command or API name")
	}
	if cmdFn, found := cmds[cmd]; found {
		cmdFn()
		return
	}

	method := os.Args[2]
	if method == "" {
		log.Fatal("Must specify API method to call")
	}

	apiName := cmd
	fs := parseArgs(os.Args[3:])
	v := flagValue(fs, "v")
	if v == "" {
		// Look up preferred version in Directory
		var err error
		v, err = getPreferredVersion(apiName)
		if err != nil {
			log.Fatal(err)
		}
	}
	api, err := loadApi(apiName, v)
	if err != nil {
		log.Fatal(err)
	}
	if api == nil || (len(api.Resources) == 0 && len(api.Methods) == 0) {
		log.Fatalf("Couldn't load API %s %s", apiName, v)
	}

	m := findMethod(method, *api)
	if m == nil {
		log.Fatalf("Can't find requested method %s", method)
	}

	m.call(fs, api)
}

func findMethod(method string, api Api) *Method {
	parts := strings.Split(method, ".")
	var ms map[string]Method
	rs := api.Resources
	for i := 0; i < len(parts)-1; i++ {
		r, found := rs[parts[i]]
		if !found {
			return nil
		}
		rs = r.Resources
		ms = r.Methods
	}
	lp := parts[len(parts)-1:][0]
	m, found := ms[lp]
	if !found {
		return nil
	}
	return &m
}

func flagValue(fs map[string]string, k string) string {
	v, found := fs[k]
	if !found {
		return ""
	}
	return v
}

func getPreferredVersion(api string) (string, error) {
	var d struct {
		Items []struct {
			Version string
		}
	}
	err := getAndParse(fmt.Sprintf("https://www.googleapis.com/discovery/v1/apis?preferred=true&name=%s&fields=items/version", api), &d)
	if err != nil {
		return "", err
	}
	return d.Items[0].Version, nil
}

func loadApi(api, version string) (*Api, error) {
	var a Api
	err := getAndParse(fmt.Sprintf("https://www.googleapis.com/discovery/v1/apis/%s/%s/rest", api, version), &a)
	if err != nil {
		return nil, err
	}
	return &a, nil
}

func getAndParse(url string, v interface{}) error {
	r, err := http.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(v)
	if err != nil {
		return err
	}
	return nil
}

type Api struct {
	BasePath   string
	Resources  map[string]Resource
	Methods    map[string]Method
	Parameters map[string]Parameter
}

type Resource struct {
	Resources map[string]Resource
	Methods   map[string]Method
}

type Method struct {
	Id, Path, HttpMethod string
	Parameters           map[string]Parameter
}

func (m Method) call(fs map[string]string, api *Api) {
	url := fmt.Sprintf("https://www.googleapis.com/%s%s", api.BasePath, m.Path)
	for k, p := range m.Parameters {
		url = p.process(k, fs, url)
	}
	for k, p := range api.Parameters {
		url = p.process(k, fs, url)
	}

	var body io.Reader
	if v, found := fs["in"]; found && v == "true" {
		// If user passes the --in flag, use stdin as the request body
		body = os.Stdin
	} else if v, found := fs["inFile"]; found {
		// If user passes --inFile flag, open that file and use its content as request body
		var err error
		body, err = os.Open(v)
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err := http.NewRequest(m.HttpMethod, url, body)
	if err != nil {
		log.Fatal(err)
	}

	client := &http.Client{}
	resp, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		io.Copy(os.Stderr, resp.Body)
		os.Exit(1)
	} else {
		io.Copy(os.Stdout, resp.Body)
	}
}

type Parameter struct {
	Type, Description, Location, Default string
	Required                             bool
}

func (p Parameter) process(k string, fs map[string]string, url string) string {
	v := flagValue(fs, k)
	if v == "" {
		v = p.Default
	}
	if v == "" {
		return url
	}
	if p.Location == "path" {
		t := fmt.Sprintf("{%s}", k)
		if p.Required && v == "" {
			log.Printf("Missing required parameter %s", k)
		}
		return strings.Replace(url, t, v, -1)
	} else if p.Location == "query" {
		if !strings.Contains(url, "?") {
			url += "?"
		}
		return url + fmt.Sprintf("&%s=%s", k, v)
	}
	return url
}
<commit_msg>Start to handle OAuth service account credentials<commit_after>// Usage: (uh, eventually, once all of this is implemented...)
// $ googlecl help  # prints help
// $ googlecl list  # lists all available APIs
// $ googlecl describe calendar                          # describes all methods available in Calendar API
// $ googlecl describe calendar calendars.get            # describes one method in Calendar API
//
// $ googlecl calendar calendars.get --calendarId=12345  # prints JSON API response
//
// $ cat someEvent.json | googlecl calendar events.insert --calendarId=12345 --in  # inserts an event
// $ googlecl calendar events.insert --calendarId=12345 --inFile=someEvent.json    # equivalent to above
//
// TODO: Handle auth somehow.
// TODO: Cache discovery/directory documents for faster requests.
// TODO: Handle media upload/download.

package main

import (
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"

	"code.google.com/p/goauth2/oauth/jwt"
)

var cmds = map[string]func(){
	"help":     func() { log.Fatal("TODO: implement help command") },
	"list":     func() { log.Fatal("TODO: implement list command") },
	"describe": func() { log.Fatal("TODO: implement describe command") },
}

func accessTokenFromPemFile(iss, scope, path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return "", err
	}

	pb, _ := pem.Decode(b)
	if len(pb.Bytes) == 0 {
		return "", errors.New("No PEM data found")
	}

	t := jwt.NewToken(iss, scope, pb.Bytes)
	tok, err := t.Assert(&http.Client{})
	return tok, err
}

func parseArgs(args []string) map[string]string {
	m := make(map[string]string)
	for _, a := range args {
		if strings.HasPrefix(a, "--") {
			a = a[2:]
		} else if strings.HasPrefix(a, "-") {
			a = a[1:]
		} else {
			log.Fatalf("Invalid flag format %s", a)
		}

		if !strings.Contains(a, "=") {
			m[a] = "true"
		} else {
			parts := strings.SplitN(a, "=", 2)
			m[parts[0]] = parts[1]
		}
	}
	return m
}

func main() {
	if len(os.Args) == 1 {
		cmds["help"]()
		return
	}

	cmd := os.Args[1]
	if cmd == "" {
		log.Fatal("Must specify command or API name")
	}
	if cmdFn, found := cmds[cmd]; found {
		cmdFn()
		return
	}

	method := os.Args[2]
	if method == "" {
		log.Fatal("Must specify API method to call")
	}

	apiName := cmd
	fs := parseArgs(os.Args[3:])
	v := flagValue(fs, "v")
	if v == "" {
		// Look up preferred version in Directory
		var err error
		v, err = getPreferredVersion(apiName)
		if err != nil {
			log.Fatal(err)
		}
	}
	api, err := loadApi(apiName, v)
	if err != nil {
		log.Fatal(err)
	}
	if api == nil || (len(api.Resources) == 0 && len(api.Methods) == 0) {
		log.Fatalf("Couldn't load API %s %s", apiName, v)
	}

	m := findMethod(method, *api)
	if m == nil {
		log.Fatalf("Can't find requested method %s", method)
	}

	m.call(fs, api)
}

func findMethod(method string, api Api) *Method {
	parts := strings.Split(method, ".")
	var ms map[string]Method
	rs := api.Resources
	for i := 0; i < len(parts)-1; i++ {
		r, found := rs[parts[i]]
		if !found {
			return nil
		}
		rs = r.Resources
		ms = r.Methods
	}
	lp := parts[len(parts)-1:][0]
	m, found := ms[lp]
	if !found {
		return nil
	}
	return &m
}

func flagValue(fs map[string]string, k string) string {
	v, found := fs[k]
	if !found {
		return ""
	}
	return v
}

func getPreferredVersion(api string) (string, error) {
	var d struct {
		Items []struct {
			Version string
		}
	}
	err := getAndParse(fmt.Sprintf("https://www.googleapis.com/discovery/v1/apis?preferred=true&name=%s&fields=items/version", api), &d)
	if err != nil {
		return "", err
	}
	return d.Items[0].Version, nil
}

func loadApi(api, version string) (*Api, error) {
	var a Api
	err := getAndParse(fmt.Sprintf("https://www.googleapis.com/discovery/v1/apis/%s/%s/rest", api, version), &a)
	if err != nil {
		return nil, err
	}
	return &a, nil
}

func getAndParse(url string, v interface{}) error {
	r, err := http.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(v)
	if err != nil {
		return err
	}
	return nil
}

type Api struct {
	BasePath   string
	Resources  map[string]Resource
	Methods    map[string]Method
	Parameters map[string]Parameter
}

type Resource struct {
	Resources map[string]Resource
	Methods   map[string]Method
}

type Method struct {
	Id, Path, HttpMethod string
	Parameters           map[string]Parameter
}

func (m Method) call(fs map[string]string, api *Api) {
	url := fmt.Sprintf("https://www.googleapis.com/%s%s", api.BasePath, m.Path)
	for k, p := range m.Parameters {
		url = p.process(k, fs, url)
	}
	for k, p := range api.Parameters {
		url = p.process(k, fs, url)
	}

	var body io.Reader
	if v, found := fs["in"]; found && v == "true" {
		// If user passes the --in flag, use stdin as the request body
		body = os.Stdin
	} else if v, found := fs["inFile"]; found {
		// If user passes --inFile flag, open that file and use its content as request body
		var err error
		body, err = os.Open(v)
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err := http.NewRequest(m.HttpMethod, url, body)
	if err != nil {
		log.Fatal(err)
	}

	client := &http.Client{}
	resp, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		io.Copy(os.Stderr, resp.Body)
		os.Exit(1)
	} else {
		io.Copy(os.Stdout, resp.Body)
	}
}

type Parameter struct {
	Type, Description, Location, Default string
	Required                             bool
}

func (p Parameter) process(k string, fs map[string]string, url string) string {
	v := flagValue(fs, k)
	if v == "" {
		v = p.Default
	}
	if v == "" {
		return url
	}
	if p.Location == "path" {
		t := fmt.Sprintf("{%s}", k)
		if p.Required && v == "" {
			log.Printf("Missing required parameter %s", k)
		}
		return strings.Replace(url, t, v, -1)
	} else if p.Location == "query" {
		if !strings.Contains(url, "?") {
			url += "?"
		}
		return url + fmt.Sprintf("&%s=%s", k, v)
	}
	return url
}
<|endoftext|>


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

package ratelimit

import (
	"errors"
	"io"

	"golang.org/x/net/context"
)

// Create a reader that limits the bandwidth of reads made from r according to
// the supplied throttler. Reads are assumed to be made under the supplied
// context.
func ThrottledReader(
	ctx context.Context,
	r io.Reader,
	throttle Throttle) io.Reader {
	return &throttledReader{
		ctx:      ctx,
		wrapped:  r,
		throttle: throttle,
	}
}

type throttledReader struct {
	ctx      context.Context
	wrapped  io.Reader
	throttle Throttle
}

func (tr *throttledReader) Read(p []byte) (n int, err error) {
	// We can't serve a read larger than the throttle's capacity.
	if uint64(len(p)) > tr.throttle.Capacity() {
		p = p[:int(tr.throttle.Capacity())]
	}

	// Wait for permission to continue.
	ok := tr.throttle.Wait(tr.ctx, uint64(len(p)))
	if !ok {
		err = errors.New("Cancelled while waiting for throttle.")
		return
	}

	// Serve the full amount we acquired from the throttle (unless we hit an
	// early error, including EOF).
	for len(p) > 0 && err == nil {
		var tmp int
		tmp, err = tr.wrapped.Read(p)

		n += tmp
		p = p[tmp:]
	}

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

package ratelimit

import (
	"io"

	"golang.org/x/net/context"
)

// Create a reader that limits the bandwidth of reads made from r according to
// the supplied throttler. Reads are assumed to be made under the supplied
// context.
func ThrottledReader(
	ctx context.Context,
	r io.Reader,
	throttle Throttle) io.Reader {
	return &throttledReader{
		ctx:      ctx,
		wrapped:  r,
		throttle: throttle,
	}
}

type throttledReader struct {
	ctx      context.Context
	wrapped  io.Reader
	throttle Throttle
}

func (tr *throttledReader) Read(p []byte) (n int, err error) {
	// We can't serve a read larger than the throttle's capacity.
	if uint64(len(p)) > tr.throttle.Capacity() {
		p = p[:int(tr.throttle.Capacity())]
	}

	// Wait for permission to continue.
	err = tr.throttle.Wait(tr.ctx, uint64(len(p)))
	if err != nil {
		return
	}

	// Serve the full amount we acquired from the throttle (unless we hit an
	// early error, including EOF).
	for len(p) > 0 && err == nil {
		var tmp int
		tmp, err = tr.wrapped.Read(p)

		n += tmp
		p = p[tmp:]
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
    //"log"
    "fmt"
    "os"
    //"time"

    "github.com/arachnist/gorepost/config"
    //"github.com/sorcix/irc"
)

func main() {
    config, err := config.ReadConfig(os.Args[1])
    if err != nil {
        fmt.Println("Error reading configuration from", os.Args[1], "error:", err.Error())
        os.Exit(1)
    }

    fmt.Println("My nickname:", config.Nick)
}
<commit_msg>I think these things should work, but yeah… not yet tested.<commit_after>package main

import (
    "log"
    "fmt"
    "os"
    "time"
    "math/rand"

    "github.com/arachnist/gorepost/config"
    "github.com/sorcix/irc"
)

type Connection struct {
    Network     string
    Input       chan irc.Message
    Output      chan irc.Message
    IRCConn     *irc.Conn
    QuitSend    chan struct{}
    QuitRecv    chan struct{}
}

func (c *Connection) Sender() {
    for {
        select {
        case msg := <-c.Input:
            c.IRCConn.Encode(&msg)
        case <-c.QuitSend:
            log.Println(c.Network, "closing Sender")
            close(c.Input)
            close(c.QuitSend)
            return
        }
    }
}

func (c *Connection) Receiver() {
    for {
        msg, err := c.IRCConn.Decode()
        if err != nil {
            log.Println(c.Network, "error decoding message", err.Error())
        }
        select {
        case c.Output <- *msg:
        case <-c.QuitRecv:
            log.Println(c.Network, "closing receiver")
            close(c.Output)
            close(c.QuitRecv)
            return
        }
    }
}

func SetupConn(network string, config config.Config, connection *Connection) (error) {
    rand.Seed(time.Now().UnixNano())
    server := config.Servers[network][rand.Intn(len(config.Servers[network]))]

    conn, err := irc.Dial(server)
    if err != nil {
        log.Println("Cannot connect to", network, "server:", server, "error:", err.Error())
        return err
    }
    connection.IRCConn = conn

    go connection.Sender()
    go connection.Receiver()

    // Initial commands sent to IRC server
    connection.Input <- irc.Message{
        Command:    "NICK",
        Trailing:   config.Nick,
    }
    connection.Input <- irc.Message{
        Command:    "USER",
        Params:     []string{config.Nick, "3", "*"},
        Trailing:   config.Nick,
    }

    return nil
}

func ConnectionKeeper(connection *Connection) {
    for {

    }
}

func main() {
    config, err := config.ReadConfig(os.Args[1])
    if err != nil {
        fmt.Println("Error reading configuration from", os.Args[1], "error:", err.Error())
        os.Exit(1)
    }

    logfile, err := os.OpenFile(config.Logpath, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("Error opening", config.Logpath,"for writing, error:", err.Error())
        os.Exit(1)
    }
    log.SetOutput(logfile)

}
<|endoftext|>


<commit_before>package reflection

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"sync"

	"github.com/golang/protobuf/proto"
	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
)

type serverReflectionServer struct {
	s *grpc.Server
	// TODO mu is not used. Add lock() and unlock().
	mu                sync.Mutex
	typeToNameMap     map[reflect.Type]string
	nameToTypeMap     map[string]reflect.Type
	typeToFileDescMap map[reflect.Type]*dpb.FileDescriptorProto
	// TODO remove this, replace with s.ftdmap
	filenameToDescMap map[string]*dpb.FileDescriptorProto
}

// InstallOnServer installs server reflection service on the given grpc server.
func InstallOnServer(s *grpc.Server) {
	rpb.RegisterServerReflectionServer(s, &serverReflectionServer{
		s:                 s,
		typeToNameMap:     make(map[reflect.Type]string),
		nameToTypeMap:     make(map[string]reflect.Type),
		typeToFileDescMap: make(map[reflect.Type]*dpb.FileDescriptorProto),
		filenameToDescMap: make(map[string]*dpb.FileDescriptorProto),
	})
}

type protoMessage interface {
	Descriptor() ([]byte, []int)
}

func (s *serverReflectionServer) fileDescForType(st reflect.Type) (*dpb.FileDescriptorProto, []int, error) {
	// Indexes list is not stored in cache.
	// So this step is needed to get idxs.
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(protoMessage)
	if !ok {
		return nil, nil, fmt.Errorf("failed to create message from type: %v", st)
	}
	enc, idxs := m.Descriptor()

	// Check type to fileDesc cache.
	if fd, ok := s.typeToFileDescMap[st]; ok {
		return fd, idxs, nil
	}

	// Cache missed, try to decode.
	fd, err := s.decodeFileDesc(enc)
	if err != nil {
		return nil, nil, err
	}
	// Add to cache.
	s.typeToFileDescMap[st] = fd
	return fd, idxs, nil
}

func (s *serverReflectionServer) decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {
	raw := decompress(enc)
	if raw == nil {
		return nil, fmt.Errorf("failed to decompress enc")
	}

	fd := new(dpb.FileDescriptorProto)
	if err := proto.Unmarshal(raw, fd); err != nil {
		return nil, fmt.Errorf("bad descriptor: %v", err)
	}
	// If decodeFileDesc is called, it's the first time this file is seen.
	// Add it to cache.
	s.filenameToDescMap[fd.GetName()] = fd
	return fd, nil
}

func decompress(b []byte) []byte {
	r, err := gzip.NewReader(bytes.NewReader(b))
	if err != nil {
		fmt.Printf("bad gzipped descriptor: %v\n", err)
		return nil
	}
	out, err := ioutil.ReadAll(r)
	if err != nil {
		fmt.Printf("bad gzipped descriptor: %v\n", err)
		return nil
	}
	return out
}

func (s *serverReflectionServer) typeForName(name string) (reflect.Type, error) {
	// Check cache first.
	if st, ok := s.nameToTypeMap[name]; ok {
		return st, nil
	}

	pt := proto.MessageType(name)
	if pt == nil {
		return nil, fmt.Errorf("unknown type: %q", name)
	}
	st := pt.Elem()

	// Add to cache.
	s.typeToNameMap[st] = name
	s.nameToTypeMap[name] = st

	// TODO is this necessary?
	// In most cases, the returned type will be used to search
	// for file descriptor.
	// Add it to cache now.
	fd, _, err := s.fileDescForType(st)
	if err == nil {
		s.typeToFileDescMap[st] = fd
	}

	return st, nil
}

func (s *serverReflectionServer) nameForType(st reflect.Type) (string, error) {
	// Check cache first.
	if name, ok := s.typeToNameMap[st]; ok {
		return name, nil
	}

	var name string
	fd, idxs, err := s.fileDescForType(st)
	if err != nil {
		return "", err
	}
	mt := fd.MessageType[idxs[0]]
	name = mt.GetName()
	for i := 1; i < len(idxs); i++ {
		mt = mt.NestedType[idxs[i]]
		name += "_" + mt.GetName()
	}
	if fd.Package != nil {
		name = *fd.Package + "." + name
	}

	// Add to cache.
	s.typeToNameMap[st] = name
	s.nameToTypeMap[name] = st

	return name, nil
}

func (s *serverReflectionServer) nameForPointer(i interface{}) (string, error) {
	return s.nameForType(reflect.TypeOf(i).Elem())
}

func (s *serverReflectionServer) filenameForType(st reflect.Type) (string, error) {
	// Check cache first. The value of cache is descriptor, not filename.
	if fd, ok := s.typeToFileDescMap[st]; ok {
		return fd.GetName(), nil
	}

	fd, _, err := s.fileDescForType(st)
	if err != nil {
		return "", err
	}
	return fd.GetName(), nil
}

// TODO filenameForMethod and Service

func (s *serverReflectionServer) fileDescContainingExtension(st reflect.Type, ext int32) (*dpb.FileDescriptorProto, error) {
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
	if !ok {
		return nil, fmt.Errorf("failed to create message from type: %v", st)
	}

	var extDesc *proto.ExtensionDesc
	for id, desc := range proto.RegisteredExtensions(m) {
		if id == ext {
			extDesc = desc
			break
		}
	}

	if extDesc == nil {
		return nil, fmt.Errorf("failed to find registered extension for extension number %v", ext)
	}

	extT := reflect.TypeOf(extDesc.ExtensionType).Elem()
	// TODO this doesn't work if extT is simple types, like int32
	// Check cache.
	if fd, ok := s.typeToFileDescMap[extT]; ok {
		return fd, nil
	}

	fd, _, err := s.fileDescForType(extT)
	if err != nil {
		return nil, err
	}
	return fd, nil
}

// TODO filenameContainingExtension
// fd := fileDescContainingExtension()
// return fd.GetName()

// fileDescWireFormatByFilename returns the file descriptor of file with the given name.
// TODO exporte and add lock
func (s *serverReflectionServer) fileDescWireFormatByFilename(name string) ([]byte, error) {
	fd, ok := s.filenameToDescMap[name]
	if !ok {
		return nil, fmt.Errorf("unknown file: %v", name)
	}
	b, err := proto.Marshal(fd)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func (s *serverReflectionServer) fileDescWireFormatContainingSymbol(name string) ([]byte, error) {
	var (
		fd *dpb.FileDescriptorProto
	)
	// Check if it's a type name.
	if st, err := s.typeForName(name); err == nil {
		fd, _, err = s.fileDescForType(st)
		if err != nil {
			return nil, err
		}
	} else {
		// Check if it's a service name or method name.
		meta := s.s.Metadata(name)
		if meta != nil {
			if enc, ok := meta.([]byte); ok {
				fd, err = s.decodeFileDesc(enc)
				if err != nil {
					return nil, err
				}
			}
		}
	}

	// Marshal to wire format.
	if fd != nil {
		b, err := proto.Marshal(fd)
		if err != nil {
			return nil, err
		}
		return b, nil
	}
	return nil, fmt.Errorf("unknown symbol: %v", name)
}

func (s *serverReflectionServer) allExtensionNumbersForType(st reflect.Type) ([]int32, error) {
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
	if !ok {
		return nil, fmt.Errorf("failed to create message from type: %v", st)
	}

	var out []int32
	for id := range proto.RegisteredExtensions(m) {
		out = append(out, id)
	}
	return out, nil
}

func (s *serverReflectionServer) ServerReflectionInfo(stream rpb.ServerReflection_ServerReflectionInfoServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}

		var response *rpb.FileDescriptorResponse
		switch req := in.MessageRequest.(type) {
		case *rpb.ServerReflectionRequest_FileByFilename:
			b, err := s.fileDescWireFormatByFilename(req.FileByFilename)
			if err != nil {
				// TODO grpc error or send message back
				return err
			}
			response = &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}}
		case *rpb.ServerReflectionRequest_FileContainingSymbol:
			b, err := s.fileDescWireFormatContainingSymbol(req.FileContainingSymbol)
			if err != nil {
				// TODO grpc error or send message back
				return err
			}
			response = &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}}
		case *rpb.ServerReflectionRequest_FileContainingExtension:
		case *rpb.ServerReflectionRequest_AllExtensionNumbersOfType:
		case *rpb.ServerReflectionRequest_ListServices:
		default:
			return grpc.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest)
		}

		out := &rpb.ServerReflectionResponse{
			ValidHost:       in.Host,
			OriginalRequest: in,
			MessageResponse: &rpb.ServerReflectionResponse_FileDescriptorResponse{
				FileDescriptorResponse: response,
			},
		}
		if err := stream.Send(out); err != nil {
			return err
		}
	}
}
<commit_msg>Add fileDescWireFormatContainingExtension and handle file_containing_extension<commit_after>package reflection

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"sync"

	"github.com/golang/protobuf/proto"
	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
)

type serverReflectionServer struct {
	s *grpc.Server
	// TODO mu is not used. Add lock() and unlock().
	mu                sync.Mutex
	typeToNameMap     map[reflect.Type]string
	nameToTypeMap     map[string]reflect.Type
	typeToFileDescMap map[reflect.Type]*dpb.FileDescriptorProto
	// TODO remove this, replace with s.ftdmap
	filenameToDescMap map[string]*dpb.FileDescriptorProto
}

// InstallOnServer installs server reflection service on the given grpc server.
func InstallOnServer(s *grpc.Server) {
	rpb.RegisterServerReflectionServer(s, &serverReflectionServer{
		s:                 s,
		typeToNameMap:     make(map[reflect.Type]string),
		nameToTypeMap:     make(map[string]reflect.Type),
		typeToFileDescMap: make(map[reflect.Type]*dpb.FileDescriptorProto),
		filenameToDescMap: make(map[string]*dpb.FileDescriptorProto),
	})
}

type protoMessage interface {
	Descriptor() ([]byte, []int)
}

func (s *serverReflectionServer) fileDescForType(st reflect.Type) (*dpb.FileDescriptorProto, []int, error) {
	// Indexes list is not stored in cache.
	// So this step is needed to get idxs.
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(protoMessage)
	if !ok {
		return nil, nil, fmt.Errorf("failed to create message from type: %v", st)
	}
	enc, idxs := m.Descriptor()

	// Check type to fileDesc cache.
	if fd, ok := s.typeToFileDescMap[st]; ok {
		return fd, idxs, nil
	}

	// Cache missed, try to decode.
	fd, err := s.decodeFileDesc(enc)
	if err != nil {
		return nil, nil, err
	}
	// Add to cache.
	s.typeToFileDescMap[st] = fd
	return fd, idxs, nil
}

func (s *serverReflectionServer) decodeFileDesc(enc []byte) (*dpb.FileDescriptorProto, error) {
	raw := decompress(enc)
	if raw == nil {
		return nil, fmt.Errorf("failed to decompress enc")
	}

	fd := new(dpb.FileDescriptorProto)
	if err := proto.Unmarshal(raw, fd); err != nil {
		return nil, fmt.Errorf("bad descriptor: %v", err)
	}
	// If decodeFileDesc is called, it's the first time this file is seen.
	// Add it to cache.
	s.filenameToDescMap[fd.GetName()] = fd
	return fd, nil
}

func decompress(b []byte) []byte {
	r, err := gzip.NewReader(bytes.NewReader(b))
	if err != nil {
		fmt.Printf("bad gzipped descriptor: %v\n", err)
		return nil
	}
	out, err := ioutil.ReadAll(r)
	if err != nil {
		fmt.Printf("bad gzipped descriptor: %v\n", err)
		return nil
	}
	return out
}

func (s *serverReflectionServer) typeForName(name string) (reflect.Type, error) {
	// Check cache first.
	if st, ok := s.nameToTypeMap[name]; ok {
		return st, nil
	}

	pt := proto.MessageType(name)
	if pt == nil {
		return nil, fmt.Errorf("unknown type: %q", name)
	}
	st := pt.Elem()

	// Add to cache.
	s.typeToNameMap[st] = name
	s.nameToTypeMap[name] = st

	// TODO is this necessary?
	// In most cases, the returned type will be used to search
	// for file descriptor.
	// Add it to cache now.
	fd, _, err := s.fileDescForType(st)
	if err == nil {
		s.typeToFileDescMap[st] = fd
	}

	return st, nil
}

func (s *serverReflectionServer) nameForType(st reflect.Type) (string, error) {
	// Check cache first.
	if name, ok := s.typeToNameMap[st]; ok {
		return name, nil
	}

	var name string
	fd, idxs, err := s.fileDescForType(st)
	if err != nil {
		return "", err
	}
	mt := fd.MessageType[idxs[0]]
	name = mt.GetName()
	for i := 1; i < len(idxs); i++ {
		mt = mt.NestedType[idxs[i]]
		name += "_" + mt.GetName()
	}
	if fd.Package != nil {
		name = *fd.Package + "." + name
	}

	// Add to cache.
	s.typeToNameMap[st] = name
	s.nameToTypeMap[name] = st

	return name, nil
}

func (s *serverReflectionServer) nameForPointer(i interface{}) (string, error) {
	return s.nameForType(reflect.TypeOf(i).Elem())
}

func (s *serverReflectionServer) filenameForType(st reflect.Type) (string, error) {
	// Check cache first. The value of cache is descriptor, not filename.
	if fd, ok := s.typeToFileDescMap[st]; ok {
		return fd.GetName(), nil
	}

	fd, _, err := s.fileDescForType(st)
	if err != nil {
		return "", err
	}
	return fd.GetName(), nil
}

// TODO filenameForMethod and Service

func (s *serverReflectionServer) fileDescContainingExtension(st reflect.Type, ext int32) (*dpb.FileDescriptorProto, error) {
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
	if !ok {
		return nil, fmt.Errorf("failed to create message from type: %v", st)
	}

	var extDesc *proto.ExtensionDesc
	for id, desc := range proto.RegisteredExtensions(m) {
		if id == ext {
			extDesc = desc
			break
		}
	}

	if extDesc == nil {
		return nil, fmt.Errorf("failed to find registered extension for extension number %v", ext)
	}

	extT := reflect.TypeOf(extDesc.ExtensionType).Elem()
	// TODO this doesn't work if extT is simple types, like int32
	// Check cache.
	if fd, ok := s.typeToFileDescMap[extT]; ok {
		return fd, nil
	}

	fd, _, err := s.fileDescForType(extT)
	if err != nil {
		return nil, err
	}
	return fd, nil
}

// TODO filenameContainingExtension
// fd := fileDescContainingExtension()
// return fd.GetName()

// fileDescWireFormatByFilename returns the file descriptor of file with the given name.
// TODO exporte and add lock
func (s *serverReflectionServer) fileDescWireFormatByFilename(name string) ([]byte, error) {
	fd, ok := s.filenameToDescMap[name]
	if !ok {
		return nil, fmt.Errorf("unknown file: %v", name)
	}
	b, err := proto.Marshal(fd)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func (s *serverReflectionServer) fileDescWireFormatContainingSymbol(name string) ([]byte, error) {
	var (
		fd *dpb.FileDescriptorProto
	)
	// Check if it's a type name.
	if st, err := s.typeForName(name); err == nil {
		fd, _, err = s.fileDescForType(st)
		if err != nil {
			return nil, err
		}
	} else {
		// Check if it's a service name or method name.
		meta := s.s.Metadata(name)
		if meta != nil {
			if enc, ok := meta.([]byte); ok {
				fd, err = s.decodeFileDesc(enc)
				if err != nil {
					return nil, err
				}
			}
		}
	}

	// Marshal to wire format.
	if fd != nil {
		b, err := proto.Marshal(fd)
		if err != nil {
			return nil, err
		}
		return b, nil
	}
	return nil, fmt.Errorf("unknown symbol: %v", name)
}

func (s *serverReflectionServer) fileDescWireFormatContainingExtension(typeName string, extNum int32) ([]byte, error) {
	st, err := s.typeForName(typeName)
	if err != nil {
		return nil, err
	}
	fd, err := s.fileDescContainingExtension(st, extNum)
	if err != nil {
		return nil, err
	}
	b, err := proto.Marshal(fd)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func (s *serverReflectionServer) allExtensionNumbersForType(st reflect.Type) ([]int32, error) {
	m, ok := reflect.Zero(reflect.PtrTo(st)).Interface().(proto.Message)
	if !ok {
		return nil, fmt.Errorf("failed to create message from type: %v", st)
	}

	var out []int32
	for id := range proto.RegisteredExtensions(m) {
		out = append(out, id)
	}
	return out, nil
}

func (s *serverReflectionServer) ServerReflectionInfo(stream rpb.ServerReflection_ServerReflectionInfoServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}

		var response *rpb.FileDescriptorResponse
		switch req := in.MessageRequest.(type) {
		case *rpb.ServerReflectionRequest_FileByFilename:
			b, err := s.fileDescWireFormatByFilename(req.FileByFilename)
			if err != nil {
				// TODO grpc error or send message back
				return err
			}
			response = &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}}
		case *rpb.ServerReflectionRequest_FileContainingSymbol:
			b, err := s.fileDescWireFormatContainingSymbol(req.FileContainingSymbol)
			if err != nil {
				// TODO grpc error or send message back
				return err
			}
			response = &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}}
		case *rpb.ServerReflectionRequest_FileContainingExtension:
			typeName := req.FileContainingExtension.ContainingType
			extNum := req.FileContainingExtension.ExtensionNumber
			b, err := s.fileDescWireFormatContainingExtension(typeName, extNum)
			if err != nil {
				// TODO grpc error or send message back
				return err
			}
			response = &rpb.FileDescriptorResponse{FileDescriptorProto: [][]byte{b}}
		case *rpb.ServerReflectionRequest_AllExtensionNumbersOfType:
		case *rpb.ServerReflectionRequest_ListServices:
		default:
			return grpc.Errorf(codes.InvalidArgument, "invalid MessageRequest: %v", in.MessageRequest)
		}

		out := &rpb.ServerReflectionResponse{
			ValidHost:       in.Host,
			OriginalRequest: in,
			MessageResponse: &rpb.ServerReflectionResponse_FileDescriptorResponse{
				FileDescriptorResponse: response,
			},
		}
		if err := stream.Send(out); err != nil {
			return err
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"github.com/richardwilkes/ui"
	"github.com/richardwilkes/ui/event"
)

type SimpleToolTip struct {
	text string
}

func NewSimpleToolTip(target ui.Widget, text string) *SimpleToolTip {
	st := &SimpleToolTip{text: text}
	target.EventHandlers().Add(event.ToolTipType, st.tooltip)
	return st
}

func (st *SimpleToolTip) tooltip(evt event.Event) {
	evt.(*event.ToolTip).SetToolTip(st.text)
}
<commit_msg>Add comments<commit_after>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"github.com/richardwilkes/ui"
	"github.com/richardwilkes/ui/event"
)

// SimpleToolTip provides an easy way to add a static tooltip to a widget.
type SimpleToolTip struct {
	// Text is the text that will be used for the tooltip.
	Text string
}

// NewSimpleToolTip adds a tooltip to the target.
func NewSimpleToolTip(target ui.Widget, text string) *SimpleToolTip {
	st := &SimpleToolTip{Text: text}
	target.EventHandlers().Add(event.ToolTipType, st.tooltip)
	return st
}

func (st *SimpleToolTip) tooltip(evt event.Event) {
	evt.(*event.ToolTip).SetToolTip(st.Text)
}
<|endoftext|>


<commit_before>package repo

import (
	"fmt"
	"time"

	"github.com/drone/drone-cli/drone/internal"
	"github.com/drone/drone-go/drone"

	"github.com/urfave/cli"
)

var repoUpdateCmd = cli.Command{
	Name:      "update",
	Usage:     "update a repository",
	ArgsUsage: "<repo/name>",
	Action:    repoUpdate,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "trusted",
			Usage: "repository is trusted",
		},
		cli.BoolFlag{
			Name:  "gated",
			Usage: "repository is gated",
		},
		cli.DurationFlag{
			Name:  "timeout",
			Usage: "repository timeout",
		},
		cli.StringFlag{
			Name:  "visibility",
			Usage: "repository visibility",
		},
		cli.StringFlag{
			Name:  "config",
			Usage: "repository configuration path (e.g. .drone.yml)",
		},
		cli.IntFlag{
			Name: "build-counter",
			Usage: "repository starting build number",
		},
		cli.BoolFlag{
			Name: "unsafe",
			Usage: "validate updating the build-counter is unsafe",
		},
	},
}

func repoUpdate(c *cli.Context) error {
	repo := c.Args().First()
	owner, name, err := internal.ParseRepo(repo)
	if err != nil {
		return err
	}

	client, err := internal.NewClient(c)
	if err != nil {
		return err
	}

	var (
		visibility = c.String("visibility")
		config     = c.String("config")
		timeout    = c.Duration("timeout")
		trusted    = c.Bool("trusted")
		gated      = c.Bool("gated")
		buildCounter   = c.Int("build-counter")
		unsafe		= c.Bool("unsafe")
	)

	patch := new(drone.RepoPatch)
	if c.IsSet("trusted") {
		patch.IsTrusted = &trusted
	}
	if c.IsSet("gated") {
		patch.IsGated = &gated
	}
	if c.IsSet("timeout") {
		v := int64(timeout / time.Minute)
		patch.Timeout = &v
	}
	if c.IsSet("config") {
		patch.Config = &config
	}
	if c.IsSet("visibility") {
		switch visibility {
		case "public", "private", "internal":
			patch.Visibility = &visibility
		}
	}
	if c.IsSet("build-counter") && !unsafe {
		fmt.Printf("Setting the build counter is an unsafe operation that could put your repository in an inconsistent state. Please use --unsafe to proceed")
	}
	if c.IsSet("build-counter") && unsafe {
		patch.BuildCounter = & buildCounter
	}

	if _, err := client.RepoPatch(owner, name, patch); err != nil {
		return err
	}
	fmt.Printf("Successfully updated repository %s/%s\n", owner, name)
	return nil
}
<commit_msg>gofmt<commit_after>package repo

import (
	"fmt"
	"time"

	"github.com/drone/drone-cli/drone/internal"
	"github.com/drone/drone-go/drone"

	"github.com/urfave/cli"
)

var repoUpdateCmd = cli.Command{
	Name:      "update",
	Usage:     "update a repository",
	ArgsUsage: "<repo/name>",
	Action:    repoUpdate,
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "trusted",
			Usage: "repository is trusted",
		},
		cli.BoolFlag{
			Name:  "gated",
			Usage: "repository is gated",
		},
		cli.DurationFlag{
			Name:  "timeout",
			Usage: "repository timeout",
		},
		cli.StringFlag{
			Name:  "visibility",
			Usage: "repository visibility",
		},
		cli.StringFlag{
			Name:  "config",
			Usage: "repository configuration path (e.g. .drone.yml)",
		},
		cli.IntFlag{
			Name:  "build-counter",
			Usage: "repository starting build number",
		},
		cli.BoolFlag{
			Name:  "unsafe",
			Usage: "validate updating the build-counter is unsafe",
		},
	},
}

func repoUpdate(c *cli.Context) error {
	repo := c.Args().First()
	owner, name, err := internal.ParseRepo(repo)
	if err != nil {
		return err
	}

	client, err := internal.NewClient(c)
	if err != nil {
		return err
	}

	var (
		visibility   = c.String("visibility")
		config       = c.String("config")
		timeout      = c.Duration("timeout")
		trusted      = c.Bool("trusted")
		gated        = c.Bool("gated")
		buildCounter = c.Int("build-counter")
		unsafe       = c.Bool("unsafe")
	)

	patch := new(drone.RepoPatch)
	if c.IsSet("trusted") {
		patch.IsTrusted = &trusted
	}
	if c.IsSet("gated") {
		patch.IsGated = &gated
	}
	if c.IsSet("timeout") {
		v := int64(timeout / time.Minute)
		patch.Timeout = &v
	}
	if c.IsSet("config") {
		patch.Config = &config
	}
	if c.IsSet("visibility") {
		switch visibility {
		case "public", "private", "internal":
			patch.Visibility = &visibility
		}
	}
	if c.IsSet("build-counter") && !unsafe {
		fmt.Printf("Setting the build counter is an unsafe operation that could put your repository in an inconsistent state. Please use --unsafe to proceed")
	}
	if c.IsSet("build-counter") && unsafe {
		patch.BuildCounter = &buildCounter
	}

	if _, err := client.RepoPatch(owner, name, patch); err != nil {
		return err
	}
	fmt.Printf("Successfully updated repository %s/%s\n", owner, name)
	return nil
}
<|endoftext|>


<commit_before>package suit

import (
	"encoding/json"
	"reflect"
	"unicode"
	"unicode/utf8"
)

type ConfigurationScreen struct {
	Title    string
	Subtitle string
	Sections []Section
	Actions  []Typed
}

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type Section struct {
	Title    string
	Subtitle string
	Contents []Typed
	Well     bool
}

type InputText struct {
	Title       string
	Subtitle    string
	Before      string
	After       string
	Placeholder string
	Name        string
	Value       interface{}
	InputType   string
	Minimum     *int
	Maximum     *int
}

func (o InputText) getType() string {
	return "inputText"
}

type OptionGroup struct {
	Title          string
	Subtitle       string
	Name           string
	MinimumChoices int
	MaximumChoices int
	Options        []OptionGroupOption
}

func (o OptionGroup) getType() string {
	return "optionGroup"
}

type OptionGroupOption struct {
	Title    string
	Subtitle string
	Value    string
	Selected bool
}

type Alert struct {
	Title        string
	Subtitle     string
	DisplayClass string
}

func (o Alert) getType() string {
	return "alert"
}

type ActionList struct {
	Title           string
	Subtitle        string
	Name            string
	Options         []ActionListOption
	PrimaryAction   Typed
	SecondaryAction Typed
}

func (o ActionList) getType() string {
	return "actionList"
}

type ActionListOption struct {
	Title    string
	Subtitle string
	Value    string
}

type InputTimeRange struct {
	Title    string
	Subtitle string
	Name     string
	Value    TimeRange
}

func (o InputTimeRange) getType() string {
	return "inputTimeRange"
}

type TimeRange struct {
	From string `json:"from"`
	To   string `json:"to"`
}

type InputHidden struct {
	Name  string
	Value string
}

func (o InputHidden) getType() string {
	return "inputHidden"
}

type CloseAction struct {
	Label string
}

func (o CloseAction) getType() string {
	return "close"
}

type ReplyAction struct {
	Label        string
	Name         string
	DisplayClass string
	DisplayIcon  string
}

func (o ReplyAction) getType() string {
	return "reply"
}

type Typed interface {
	getType() string
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}
<commit_msg>suit: inputTime, separator and radioGroup<commit_after>package suit

import (
	"encoding/json"
	"reflect"
	"unicode"
	"unicode/utf8"
)

type ConfigurationScreen struct {
	Title    string
	Subtitle string
	Sections []Section
	Actions  []Typed
}

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type Section struct {
	Title    string
	Subtitle string
	Contents []Typed
	Well     bool
}

type InputText struct {
	Title       string
	Subtitle    string
	Before      string
	After       string
	Placeholder string
	Name        string
	Value       interface{}
	InputType   string
	Minimum     *int
	Maximum     *int
}

func (o InputText) getType() string {
	return "inputText"
}

type InputTime struct {
	Title    string
	Subtitle string
	Before   string
	After    string
	Name     string
	Value    string
}

func (o InputTime) getType() string {
	return "inputTime"
}

type Separator struct {
}

func (o Separator) getType() string {
	return "separator"
}

type OptionGroup struct {
	Title          string
	Subtitle       string
	Name           string
	MinimumChoices int
	MaximumChoices int
	Options        []OptionGroupOption
}

func (o OptionGroup) getType() string {
	return "optionGroup"
}

type OptionGroupOption struct {
	Title    string
	Subtitle string
	Value    string
	Selected bool
}

type RadioGroup struct {
	Title    string
	Subtitle string
	Name     string
	Value    string
	Options  []RadioGroupOption
}

func (o RadioGroup) getType() string {
	return "radioGroup"
}

type RadioGroupOption struct {
	Title       string
	Value       string
	DisplayIcon string
}

type Alert struct {
	Title        string
	Subtitle     string
	DisplayClass string
}

func (o Alert) getType() string {
	return "alert"
}

type ActionList struct {
	Title           string
	Subtitle        string
	Name            string
	Options         []ActionListOption
	PrimaryAction   Typed
	SecondaryAction Typed
}

func (o ActionList) getType() string {
	return "actionList"
}

type ActionListOption struct {
	Title    string
	Subtitle string
	Value    string
}

type InputTimeRange struct {
	Title    string
	Subtitle string
	Name     string
	Value    TimeRange
}

func (o InputTimeRange) getType() string {
	return "inputTimeRange"
}

type TimeRange struct {
	From string `json:"from"`
	To   string `json:"to"`
}

type InputHidden struct {
	Name  string
	Value string
}

func (o InputHidden) getType() string {
	return "inputHidden"
}

type CloseAction struct {
	Label string
}

func (o CloseAction) getType() string {
	return "close"
}

type ReplyAction struct {
	Label        string
	Name         string
	DisplayClass string
	DisplayIcon  string
}

func (o ReplyAction) getType() string {
	return "reply"
}

type Typed interface {
	getType() string
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}
<|endoftext|>