<commit_before>package main

import (
	"fmt"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

const MSG_BUFFER int = 10

const HELP_TEXT string = `-> Available commands:
   /about
   /exit
   /help
   /list
   /nick $NAME
   /whois $NAME
`

const ABOUT_TEXT string = `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
`

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
	}
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", name))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = "is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.Name, me)
				if c.IsSilenced() {
					c.Msg <- fmt.Sprintf("-> Message rejected, silenced.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.Name, client.Fingerprint(), client.Conn.ClientVersion())
					} else {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					}
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Banned by %s.", c.Name))
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.Name), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Made op by %s.", c.Name))
						c.Server.Op(fingerprint)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.Name))
					}
				}
			default:
				c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.Name, line)
		if c.IsSilenced() {
			c.Msg <- fmt.Sprintf("-> Message rejected, silenced.")
			continue
		}
		c.Server.Broadcast(msg, c)
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.Name)

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}
<commit_msg>Spaces are hard.<commit_after>package main

import (
	"fmt"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

const MSG_BUFFER int = 10

const HELP_TEXT string = `-> Available commands:
   /about
   /exit
   /help
   /list
   /nick $NAME
   /whois $NAME
`

const ABOUT_TEXT string = `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
`

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
	}
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", name))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = " is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.Name, me)
				if c.IsSilenced() {
					c.Msg <- fmt.Sprintf("-> Message rejected, silenced.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.Name, client.Fingerprint(), client.Conn.ClientVersion())
					} else {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					}
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Banned by %s.", c.Name))
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.Name), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Made op by %s.", c.Name))
						c.Server.Op(fingerprint)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.Name))
					}
				}
			default:
				c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.Name, line)
		if c.IsSilenced() {
			c.Msg <- fmt.Sprintf("-> Message rejected, silenced.")
			continue
		}
		c.Server.Broadcast(msg, c)
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.Name)

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/ed25519"
	"crypto/rand"
	"encoding/json"
	"fmt"

	"github.com/ipfs/go-cid"
	"github.com/multiformats/go-multibase"
	"github.com/multiformats/go-multihash"
	"github.com/multiformats/go-varint"
	"github.com/teserakt-io/golang-ed25519/extra25519"
	"golang.org/x/crypto/curve25519"
)

// https://blog.filippo.io/using-ed25519-keys-for-encryption
// https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519
// http://moderncrypto.org/mail-archive/curves/2014/000205.html
// https://signal.org/docs/specifications/xeddsa
// https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519

// we are opting for ed to x at this point based on FiloSottile's age spec

type (
	PublicKey struct {
		Type      KeyType
		Algorithm KeyAlgorithm
		RawKey    ed25519.PublicKey
	}
	PrivateKey struct {
		Type      KeyType
		Algorithm KeyAlgorithm
		RawKey    ed25519.PrivateKey
	}
)

func (k PublicKey) String() string {
	return encodeToCID(uint64(k.Algorithm), uint64(k.Type), k.RawKey)
}

func (k PublicKey) MarshalString() (string, error) {
	return k.String(), nil
}

func (k PublicKey) MarshalJSON() ([]byte, error) {
	return json.Marshal(k.String())
}

func (k *PublicKey) UnmarshalString(s string) error {
	c, err := cid.Decode(s)
	if err != nil {
		return err
	}

	if c.Type() != uint64(Ed25519Public) {
		return ErrUnsupportedKeyAlgorithm
	}

	h, err := multihash.Decode(c.Hash())
	if err != nil {
		return err
	}

	k.Algorithm = Ed25519Public
	k.RawKey = ed25519.PublicKey(h.Digest)
	k.Type = KeyType(h.Code)

	return nil
}

func (k *PublicKey) UnmarshalJSON(s []byte) error {
	v := ""
	if err := json.Unmarshal(s, &v); err != nil {
		return err
	}
	return k.UnmarshalString(v)
}

func (k PrivateKey) String() string {
	return encodeToCID(uint64(k.Algorithm), uint64(k.Type), k.RawKey)
}

func (k PrivateKey) MarshalString() (string, error) {
	return k.String(), nil
}

func (k PrivateKey) MarshalJSON() ([]byte, error) {
	return json.Marshal(k.String())
}

func (k *PrivateKey) UnmarshalString(s string) error {
	c, err := cid.Decode(s)
	if err != nil {
		return err
	}

	if c.Type() != uint64(Ed25519Private) {
		return ErrUnsupportedKeyAlgorithm
	}

	h, err := multihash.Decode(c.Hash())
	if err != nil {
		return err
	}

	k.Algorithm = Ed25519Private
	k.RawKey = ed25519.PrivateKey(h.Digest)
	k.Type = KeyType(h.Code)

	return nil
}

func (k *PrivateKey) UnmarshalJSON(s []byte) error {
	v := ""
	if err := json.Unmarshal(s, &v); err != nil {
		return err
	}
	return k.UnmarshalString(v)
}

func (k PrivateKey) PublicKey() *PublicKey {
	return &PublicKey{
		Algorithm: Ed25519Public,
		Type:      k.Type,
		RawKey:    k.RawKey.Public().(ed25519.PublicKey),
	}
}

func NewEd25519PrivateKey(keyType KeyType) (*PrivateKey, error) {
	_, k, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, err
	}
	return &PrivateKey{
		Algorithm: Ed25519Private,
		Type:      keyType,
		RawKey:    k,
	}, nil
}

func NewEd25519PrivateKeyFromSeed(
	seed []byte,
	keyType KeyType,
) *PrivateKey {
	b := ed25519.NewKeyFromSeed(seed)
	return &PrivateKey{
		Algorithm: Ed25519Private,
		Type:      keyType,
		RawKey:    b,
	}
}

func NewEd25519PublicKeyFromRaw(
	raw ed25519.PublicKey,
	keyType KeyType,
) *PublicKey {
	return &PublicKey{
		Algorithm: Ed25519Public,
		Type:      keyType,
		RawKey:    raw,
	}
}

func publicEd25519KeyToCurve25519(pub ed25519.PublicKey) []byte {
	var edPk [ed25519.PublicKeySize]byte
	var curveKey [32]byte
	copy(edPk[:], pub)
	if !extra25519.PublicKeyToCurve25519(&curveKey, &edPk) {
		panic("could not convert ed25519 public key to curve25519")
	}
	return curveKey[:]
}

func privateEd25519KeyToCurve25519(priv ed25519.PrivateKey) []byte {
	var edSk [ed25519.PrivateKeySize]byte
	var curveKey [32]byte
	copy(edSk[:], priv)
	extra25519.PrivateKeyToCurve25519(&curveKey, &edSk)
	return curveKey[:]
}

// CalculateSharedKey calculates a shared secret given a private an public key
func CalculateSharedKey(
	priv *PrivateKey,
	pub *PublicKey,
) ([]byte, error) {
	if priv.Algorithm != Ed25519Private || pub.Algorithm != Ed25519Public {
		return nil, ErrUnsupportedKeyAlgorithm
	}
	ca := privateEd25519KeyToCurve25519(priv.RawKey)
	cB := publicEd25519KeyToCurve25519(pub.RawKey)
	ss, err := curve25519.X25519(ca, cB)
	if err != nil {
		return nil, fmt.Errorf("error getting x25519, %w", err)
	}
	return ss, nil
}

// NewSharedKey calculates a shared secret given a private and a public key,
// and returns it
func NewSharedKey(
	priv *PrivateKey,
	pub *PublicKey,
) (*PrivateKey, []byte, error) {
	ca := privateEd25519KeyToCurve25519(priv.RawKey)
	cB := publicEd25519KeyToCurve25519(pub.RawKey)
	ss, err := curve25519.X25519(ca, cB)
	if err != nil {
		return nil, nil, err
	}
	return priv, ss, nil
}

// CalculateEphemeralSharedKey creates a new ec25519 key pair, calculates a
// shared secret given a public key, and returns the created public key and
// secret
func CalculateEphemeralSharedKey(
	pub *PublicKey,
) (*PrivateKey, []byte, error) {
	priv, err := NewEd25519PrivateKey(PeerKey)
	if err != nil {
		return nil, nil, err
	}
	return NewSharedKey(priv, pub)
}

func (k PrivateKey) Sign(message []byte) []byte {
	return ed25519.Sign(k.RawKey, message)
}

func (k PublicKey) Verify(message []byte, signature []byte) error {
	ok := ed25519.Verify(k.RawKey, message, signature)
	if !ok {
		return ErrInvalidSignature
	}
	return nil
}

func (k PublicKey) Equals(w *PublicKey) bool {
	return k.Algorithm == w.Algorithm && k.Type == w.Type && k.RawKey.Equal(w.RawKey)
}

func encodeToCID(cidCode, multihashCode uint64, raw []byte) string {
	mh := make(
		[]byte,
		varint.UvarintSize(multihashCode)+
			varint.UvarintSize(uint64(len(raw)))+
			len(raw),
	)
	n := varint.PutUvarint(mh, multihashCode)
	n += varint.PutUvarint(mh[n:], uint64(len(raw)))
	copy(mh[n:], raw)
	c := cid.NewCidV1(cidCode, mh)
	// nolint: errcheck // cannot error
	s, _ := multibase.Encode(multibase.Base32, c.Bytes())
	return s
}
<commit_msg>chore(crypto): fix long line<commit_after>package crypto

import (
	"crypto/ed25519"
	"crypto/rand"
	"encoding/json"
	"fmt"

	"github.com/ipfs/go-cid"
	"github.com/multiformats/go-multibase"
	"github.com/multiformats/go-multihash"
	"github.com/multiformats/go-varint"
	"github.com/teserakt-io/golang-ed25519/extra25519"
	"golang.org/x/crypto/curve25519"
)

// https://blog.filippo.io/using-ed25519-keys-for-encryption
// https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519
// http://moderncrypto.org/mail-archive/curves/2014/000205.html
// https://signal.org/docs/specifications/xeddsa
// https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519

// we are opting for ed to x at this point based on FiloSottile's age spec

type (
	PublicKey struct {
		Type      KeyType
		Algorithm KeyAlgorithm
		RawKey    ed25519.PublicKey
	}
	PrivateKey struct {
		Type      KeyType
		Algorithm KeyAlgorithm
		RawKey    ed25519.PrivateKey
	}
)

func (k PublicKey) String() string {
	return encodeToCID(uint64(k.Algorithm), uint64(k.Type), k.RawKey)
}

func (k PublicKey) MarshalString() (string, error) {
	return k.String(), nil
}

func (k PublicKey) MarshalJSON() ([]byte, error) {
	return json.Marshal(k.String())
}

func (k *PublicKey) UnmarshalString(s string) error {
	c, err := cid.Decode(s)
	if err != nil {
		return err
	}

	if c.Type() != uint64(Ed25519Public) {
		return ErrUnsupportedKeyAlgorithm
	}

	h, err := multihash.Decode(c.Hash())
	if err != nil {
		return err
	}

	k.Algorithm = Ed25519Public
	k.RawKey = ed25519.PublicKey(h.Digest)
	k.Type = KeyType(h.Code)

	return nil
}

func (k *PublicKey) UnmarshalJSON(s []byte) error {
	v := ""
	if err := json.Unmarshal(s, &v); err != nil {
		return err
	}
	return k.UnmarshalString(v)
}

func (k PrivateKey) String() string {
	return encodeToCID(uint64(k.Algorithm), uint64(k.Type), k.RawKey)
}

func (k PrivateKey) MarshalString() (string, error) {
	return k.String(), nil
}

func (k PrivateKey) MarshalJSON() ([]byte, error) {
	return json.Marshal(k.String())
}

func (k *PrivateKey) UnmarshalString(s string) error {
	c, err := cid.Decode(s)
	if err != nil {
		return err
	}

	if c.Type() != uint64(Ed25519Private) {
		return ErrUnsupportedKeyAlgorithm
	}

	h, err := multihash.Decode(c.Hash())
	if err != nil {
		return err
	}

	k.Algorithm = Ed25519Private
	k.RawKey = ed25519.PrivateKey(h.Digest)
	k.Type = KeyType(h.Code)

	return nil
}

func (k *PrivateKey) UnmarshalJSON(s []byte) error {
	v := ""
	if err := json.Unmarshal(s, &v); err != nil {
		return err
	}
	return k.UnmarshalString(v)
}

func (k PrivateKey) PublicKey() *PublicKey {
	return &PublicKey{
		Algorithm: Ed25519Public,
		Type:      k.Type,
		RawKey:    k.RawKey.Public().(ed25519.PublicKey),
	}
}

func NewEd25519PrivateKey(keyType KeyType) (*PrivateKey, error) {
	_, k, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, err
	}
	return &PrivateKey{
		Algorithm: Ed25519Private,
		Type:      keyType,
		RawKey:    k,
	}, nil
}

func NewEd25519PrivateKeyFromSeed(
	seed []byte,
	keyType KeyType,
) *PrivateKey {
	b := ed25519.NewKeyFromSeed(seed)
	return &PrivateKey{
		Algorithm: Ed25519Private,
		Type:      keyType,
		RawKey:    b,
	}
}

func NewEd25519PublicKeyFromRaw(
	raw ed25519.PublicKey,
	keyType KeyType,
) *PublicKey {
	return &PublicKey{
		Algorithm: Ed25519Public,
		Type:      keyType,
		RawKey:    raw,
	}
}

func publicEd25519KeyToCurve25519(pub ed25519.PublicKey) []byte {
	var edPk [ed25519.PublicKeySize]byte
	var curveKey [32]byte
	copy(edPk[:], pub)
	if !extra25519.PublicKeyToCurve25519(&curveKey, &edPk) {
		panic("could not convert ed25519 public key to curve25519")
	}
	return curveKey[:]
}

func privateEd25519KeyToCurve25519(priv ed25519.PrivateKey) []byte {
	var edSk [ed25519.PrivateKeySize]byte
	var curveKey [32]byte
	copy(edSk[:], priv)
	extra25519.PrivateKeyToCurve25519(&curveKey, &edSk)
	return curveKey[:]
}

// CalculateSharedKey calculates a shared secret given a private an public key
func CalculateSharedKey(
	priv *PrivateKey,
	pub *PublicKey,
) ([]byte, error) {
	if priv.Algorithm != Ed25519Private || pub.Algorithm != Ed25519Public {
		return nil, ErrUnsupportedKeyAlgorithm
	}
	ca := privateEd25519KeyToCurve25519(priv.RawKey)
	cB := publicEd25519KeyToCurve25519(pub.RawKey)
	ss, err := curve25519.X25519(ca, cB)
	if err != nil {
		return nil, fmt.Errorf("error getting x25519, %w", err)
	}
	return ss, nil
}

// NewSharedKey calculates a shared secret given a private and a public key,
// and returns it
func NewSharedKey(
	priv *PrivateKey,
	pub *PublicKey,
) (*PrivateKey, []byte, error) {
	ca := privateEd25519KeyToCurve25519(priv.RawKey)
	cB := publicEd25519KeyToCurve25519(pub.RawKey)
	ss, err := curve25519.X25519(ca, cB)
	if err != nil {
		return nil, nil, err
	}
	return priv, ss, nil
}

// CalculateEphemeralSharedKey creates a new ec25519 key pair, calculates a
// shared secret given a public key, and returns the created public key and
// secret
func CalculateEphemeralSharedKey(
	pub *PublicKey,
) (*PrivateKey, []byte, error) {
	priv, err := NewEd25519PrivateKey(PeerKey)
	if err != nil {
		return nil, nil, err
	}
	return NewSharedKey(priv, pub)
}

func (k PrivateKey) Sign(message []byte) []byte {
	return ed25519.Sign(k.RawKey, message)
}

func (k PublicKey) Verify(message []byte, signature []byte) error {
	ok := ed25519.Verify(k.RawKey, message, signature)
	if !ok {
		return ErrInvalidSignature
	}
	return nil
}

func (k PublicKey) Equals(w *PublicKey) bool {
	return k.Algorithm == w.Algorithm &&
		k.Type == w.Type &&
		k.RawKey.Equal(w.RawKey)
}

func encodeToCID(cidCode, multihashCode uint64, raw []byte) string {
	mh := make(
		[]byte,
		varint.UvarintSize(multihashCode)+
			varint.UvarintSize(uint64(len(raw)))+
			len(raw),
	)
	n := varint.PutUvarint(mh, multihashCode)
	n += varint.PutUvarint(mh[n:], uint64(len(raw)))
	copy(mh[n:], raw)
	c := cid.NewCidV1(cidCode, mh)
	// nolint: errcheck // cannot error
	s, _ := multibase.Encode(multibase.Base32, c.Bytes())
	return s
}
<|endoftext|>


<commit_before>package plugins

import (
	"net/url"
	"path"
)

type FrontendPluginBase struct {
	PluginBase
	Module     string `json:"module"`
	StaticRoot string `json:"staticRoot"`
}

func (fp *FrontendPluginBase) initFrontendPlugin() {
	if fp.StaticRoot != "" {
		StaticRoutes = append(StaticRoutes, &PluginStaticRoute{
			Directory: fp.StaticRoot,
			PluginId:  fp.Id,
		})
	}

	fp.Info.Logos.Small = evalRelativePluginUrlPath(fp.Info.Logos.Small, fp.Id)
	fp.Info.Logos.Large = evalRelativePluginUrlPath(fp.Info.Logos.Large, fp.Id)

	fp.handleModuleDefaults()
}

func (fp *FrontendPluginBase) handleModuleDefaults() {
	if fp.Module != "" {
		return
	}

	if fp.StaticRoot != "" {
		fp.Module = path.Join("plugins", fp.Type, fp.Id, "module")
		return
	}

	fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module")
}

func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
	u, _ := url.Parse(pathStr)
	if u.IsAbs() {
		return pathStr
	}
	return path.Join("public/plugins", pluginId, pathStr)
}
<commit_msg>feat(plugins): minor fix for external plugins with staticRoot<commit_after>package plugins

import (
	"net/url"
	"path"
	"path/filepath"
)

type FrontendPluginBase struct {
	PluginBase
	Module     string `json:"module"`
	StaticRoot string `json:"staticRoot"`
}

func (fp *FrontendPluginBase) initFrontendPlugin() {
	if fp.StaticRoot != "" {
		StaticRoutes = append(StaticRoutes, &PluginStaticRoute{
			Directory: filepath.Join(fp.PluginDir, fp.StaticRoot),
			PluginId:  fp.Id,
		})
	}

	fp.Info.Logos.Small = evalRelativePluginUrlPath(fp.Info.Logos.Small, fp.Id)
	fp.Info.Logos.Large = evalRelativePluginUrlPath(fp.Info.Logos.Large, fp.Id)

	fp.handleModuleDefaults()
}

func (fp *FrontendPluginBase) handleModuleDefaults() {
	if fp.Module != "" {
		return
	}

	if fp.StaticRoot != "" {
		fp.Module = path.Join("plugins", fp.Id, "module")
		return
	}

	fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module")
}

func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
	u, _ := url.Parse(pathStr)
	if u.IsAbs() {
		return pathStr
	}
	return path.Join("public/plugins", pluginId, pathStr)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Nuclio Authors.

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 v3io

import (
	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/processor/databinding"
)

type Configuration struct {
	databinding.Configuration
	NumWorkers int
	Username   string
	Password   string
}

func NewConfiguration(ID string, databindingConfiguration *functionconfig.DataBinding) (*Configuration, error) {
	newConfiguration := Configuration{}

	// create base
	newConfiguration.Configuration = *databinding.NewConfiguration(ID, databindingConfiguration)

	// set default num workers
	if newConfiguration.NumWorkers == 0 {
		newConfiguration.NumWorkers = 8
	}

	return &newConfiguration, nil
}
<commit_msg>Properly decode v3io databinding (#919)<commit_after>/*
Copyright 2017 The Nuclio Authors.

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 v3io

import (
	"github.com/nuclio/nuclio/pkg/errors"
	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/processor/databinding"

	"github.com/mitchellh/mapstructure"
)

type Configuration struct {
	databinding.Configuration
	NumWorkers int
	Username   string
	Password   string
}

func NewConfiguration(ID string, databindingConfiguration *functionconfig.DataBinding) (*Configuration, error) {
	newConfiguration := Configuration{}

	// create base
	newConfiguration.Configuration = *databinding.NewConfiguration(ID, databindingConfiguration)

	// parse attributes
	if err := mapstructure.Decode(newConfiguration.Configuration.Attributes, &newConfiguration); err != nil {
		return nil, errors.Wrap(err, "Failed to decode attributes")
	}

	// set default num workers
	if newConfiguration.NumWorkers == 0 {
		newConfiguration.NumWorkers = 8
	}

	return &newConfiguration, nil
}
<|endoftext|>


<commit_before>package conntrack

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"syscall"
	"unsafe"
)

type nfgenmsg struct {
	Family  uint8  /* AF_xxx */
	Version uint8  /* nfnetlink version */
	ResID   uint16 /* resource id */
}

const (
	sizeofGenmsg = uint32(unsafe.Sizeof(nfgenmsg{})) // TODO
)

type ConntrackListReq struct {
	Header syscall.NlMsghdr
	Body   nfgenmsg
}

func (c *ConntrackListReq) toWireFormat() []byte {
	// adapted from syscall/NetlinkRouteRequest.toWireFormat
	b := make([]byte, c.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = c.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = c.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = c.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = c.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = c.Header.Pid
	b[16] = byte(c.Body.Family)
	b[17] = byte(c.Body.Version)
	*(*uint16)(unsafe.Pointer(&b[18:20][0])) = c.Body.ResID
	return b
}

func connectNetfilter(groups uint32) (int, *syscall.SockaddrNetlink, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
	if err != nil {
		return 0, nil, err
	}
	lsa := &syscall.SockaddrNetlink{
		Family: syscall.AF_NETLINK,
		Groups: groups,
	}
	if err := syscall.Bind(s, lsa); err != nil {
		return 0, nil, err
	}

	return s, lsa, nil
}

// Established lists all established TCP connections.
func Established() ([]ConnTCP, error) {
	s, lsa, err := connectNetfilter(0)
	if err != nil {
		return nil, err
	}
	defer syscall.Close(s)

	var conns []ConnTCP
	msg := ConntrackListReq{
		Header: syscall.NlMsghdr{
			Len:   syscall.NLMSG_HDRLEN + sizeofGenmsg,
			Type:  (NFNL_SUBSYS_CTNETLINK << 8) | uint16(IpctnlMsgCtGet),
			Flags: syscall.NLM_F_REQUEST | syscall.NLM_F_DUMP,
			Pid:   0,
			Seq:   0,
		},
		Body: nfgenmsg{
			Family:  syscall.AF_INET,
			Version: NFNETLINK_V0,
			ResID:   0,
		},
	}
	wb := msg.toWireFormat()
	// fmt.Printf("msg bytes: %q\n", wb)
	if err := syscall.Sendto(s, wb, 0, lsa); err != nil {
		return nil, err
	}

	local := localIPs()
	readMsgs(s, func(c Conn) {
		if c.MsgType != NfctMsgUpdate {
			fmt.Printf("msg isn't an update: %d\n", c.MsgType)
			return
		}
		if c.TCPState != "ESTABLISHED" {
			// fmt.Printf("state isn't ESTABLISHED: %s\n", c.TCPState)
			return
		}
		if tc := c.ConnTCP(local); tc != nil {
			conns = append(conns, *tc)
		}
	})
	return conns, nil
}

// Follow gives a channel with all changes.
func Follow() (<-chan Conn, func(), error) {
	s, _, err := connectNetfilter(
		NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_UPDATE |
		NF_NETLINK_CONNTRACK_DESTROY)
	stop := func() {
		syscall.Close(s)
	}
	if err != nil {
		return nil, stop, err
	}

	res := make(chan Conn, 1)
	go func() {
		defer syscall.Close(s)
		if err := readMsgs(s, func(c Conn) {
			// if conn.TCPState != 3 {
			// // 3 is TCP established.
			// continue
			// }
			res <- c
		}); err != nil {
			panic(err)
		}
	}()
	return res, stop, nil
}

func readMsgs(s int, cb func(Conn)) error {
	for {
		rb := make([]byte, 2*syscall.Getpagesize()) // TODO: re-use
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return err
		}

		msgs, err := syscall.ParseNetlinkMessage(rb[:nr])
		if err != nil {
			return err
		}
		for _, msg := range msgs {
			if err := nfnlIsError(msg.Header); err != nil {
				return fmt.Errorf("msg is some error: %s\n", err)
			}
			if nflnSubsysID(msg.Header.Type) != NFNL_SUBSYS_CTNETLINK {
				return fmt.Errorf(
					"unexpected subsys_id: %d\n",
					nflnSubsysID(msg.Header.Type),
				)
			}
			conn, err := parsePayload(msg.Data[sizeofGenmsg:])
			if err != nil {
				return err
			}

			// Taken from conntrack/parse.c:__parse_message_type
			switch CntlMsgTypes(nflnMsgType(msg.Header.Type)) {
			case IpctnlMsgCtNew:
				conn.MsgType = NfctMsgUpdate
				if msg.Header.Flags&(syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) > 0 {
					conn.MsgType = NfctMsgNew
				}
			case IpctnlMsgCtDelete:
				conn.MsgType = NfctMsgDestroy
			}

			cb(*conn)
		}
	}
}

type Conn struct {
	MsgType  NfConntrackMsg
	Proto    int
	Src      net.IP
	SrcPort  uint16
	Dst      net.IP
	DstPort  uint16
	TCPState string

	// ICMP stuff.
	IcmpId   uint16
	IcmpType uint8
	IcmpCode uint8

	// ct.mark, used to set permission type of the flow.
	CtMark uint32

	// ct.id, used to identify connections.
	CtId uint32

	// For multitenancy.
	Zone uint16

	// Flow stats.
	ReplyPktLen uint64
	ReplyPktCount uint64
	OrigPktLen uint64
	OrigPktCount uint64
}

// ConnTCP decides which way this connection is going and makes a ConnTCP.
func (c Conn) ConnTCP(local map[string]struct{}) *ConnTCP {
	// conntrack gives us all connections, even things passing through, but it
	// doesn't tell us what the local IP is. So we use `local` as a guide
	// what's local.
	src := c.Src.String()
	dst := c.Dst.String()
	_, srcLocal := local[src]
	_, dstLocal := local[dst]
	// If both are local we must just order things predictably.
	if srcLocal && dstLocal {
		srcLocal = c.SrcPort < c.DstPort
	}
	if srcLocal {
		return &ConnTCP{
			Local:      src,
			LocalPort:  strconv.Itoa(int(c.SrcPort)),
			Remote:     dst,
			RemotePort: strconv.Itoa(int(c.DstPort)),
		}
	}
	if dstLocal {
		return &ConnTCP{
			Local:      dst,
			LocalPort:  strconv.Itoa(int(c.DstPort)),
			Remote:     src,
			RemotePort: strconv.Itoa(int(c.SrcPort)),
		}
	}
	// Neither is local. conntrack also reports NAT connections.
	return nil
}

func parsePayload(b []byte) (*Conn, error) {
	// Most of this comes from libnetfilter_conntrack/src/conntrack/parse_mnl.c
	conn := &Conn{}
	attrs, err := parseAttrs(b)
	if err != nil {
		return conn, err
	}
	for _, attr := range attrs {
		switch CtattrType(attr.Typ) {
		case CtaTupleOrig:
		case CtaTupleReply:
			// fmt.Printf("It's a reply\n")
			// We take the reply, nor the orig.... Sure?
			parseTuple(attr.Msg, conn)
		case CtaCountersOrig:
			conn.OrigPktLen, conn.OrigPktCount, _ = parseCounters(attr.Msg)
		case CtaCountersReply:
			conn.ReplyPktLen, conn.ReplyPktCount, _ = parseCounters(attr.Msg)
		case CtaStatus:
			// These are ip_conntrack_status
			// status := binary.BigEndian.Uint32(attr.Msg)
			// fmt.Printf("It's status %d\n", status)
		case CtaProtoinfo:
			parseProtoinfo(attr.Msg, conn)
		case CtaMark:
			conn.CtMark = binary.BigEndian.Uint32(attr.Msg)
		case CtaZone:
			conn.Zone = binary.BigEndian.Uint16(attr.Msg)
		case CtaId:
			conn.CtId = binary.BigEndian.Uint32(attr.Msg)
		}
	}
	return conn, nil
}

func parseTuple(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		// fmt.Printf("pl: %d, type: %d, multi: %t, bigend: %t\n", len(attr.Msg), attr.Typ, attr.IsNested, attr.IsNetByteorder)
		switch CtattrTuple(attr.Typ) {
		case CtaTupleUnspec:
			// fmt.Printf("It's a tuple unspec\n")
		case CtaTupleIp:
			// fmt.Printf("It's a tuple IP\n")
			if err := parseIP(attr.Msg, conn); err != nil {
				return err
			}
		case CtaTupleProto:
			// fmt.Printf("It's a tuple proto\n")
			parseProto(attr.Msg, conn)
		}
	}
	return nil
}

func parseCounters(b []byte) (uint64, uint64, error) {
	attrs, err := parseAttrs(b)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid tuple attr: %s", err)
	}
	packets := uint64(0)
	bytes := uint64(0)
	for _, attr := range attrs {
		switch CtattrCounters(attr.Typ) {
		case CtaCountersPackets:
			packets = binary.BigEndian.Uint64(attr.Msg)
		case CtaCountersBytes:
			bytes = binary.BigEndian.Uint64(attr.Msg)
		}
	}
	return packets, bytes, nil
}

func parseIP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrIp(attr.Typ) {
		case CtaIpV4Src:
			conn.Src = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV4Dst:
			conn.Dst = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV6Src:
			// TODO
		case CtaIpV6Dst:
			// TODO
		}
	}
	return nil
}

func parseProto(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrL4proto(attr.Typ) {
		// Protocol number.
		case CtaProtoNum:
			conn.Proto = int(uint8(attr.Msg[0]))

		// TCP stuff.
		case CtaProtoSrcPort:
			conn.SrcPort = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoDstPort:
			conn.DstPort = binary.BigEndian.Uint16(attr.Msg)

		// ICMP stuff.
		case CtaProtoIcmpId:
			conn.IcmpId = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoIcmpType:
			conn.IcmpType = uint8(attr.Msg[0])
		case CtaProtoIcmpCode:
			conn.IcmpCode = uint8(attr.Msg[0])
		}
	}
	return nil
}

func parseProtoinfo(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfo(attr.Typ) {
		case CtaProtoinfoTcp:
			if err := parseProtoinfoTCP(attr.Msg, conn); err != nil {
				return err
			}
		default:
			// we're not interested in other protocols
		}
	}
	return nil
}

func parseProtoinfoTCP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfoTcp(attr.Typ) {
		case CtaProtoinfoTcpState:
			conn.TCPState = tcpState[uint8(attr.Msg[0])]
		default:
			// not interested
		}
	}
	return nil
}
<commit_msg>add API Connections() which returns list of connections found in conntrack<commit_after>package conntrack

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"syscall"
	"unsafe"
)

type nfgenmsg struct {
	Family  uint8  /* AF_xxx */
	Version uint8  /* nfnetlink version */
	ResID   uint16 /* resource id */
}

const (
	sizeofGenmsg = uint32(unsafe.Sizeof(nfgenmsg{})) // TODO
)

type ConntrackListReq struct {
	Header syscall.NlMsghdr
	Body   nfgenmsg
}

func (c *ConntrackListReq) toWireFormat() []byte {
	// adapted from syscall/NetlinkRouteRequest.toWireFormat
	b := make([]byte, c.Header.Len)
	*(*uint32)(unsafe.Pointer(&b[0:4][0])) = c.Header.Len
	*(*uint16)(unsafe.Pointer(&b[4:6][0])) = c.Header.Type
	*(*uint16)(unsafe.Pointer(&b[6:8][0])) = c.Header.Flags
	*(*uint32)(unsafe.Pointer(&b[8:12][0])) = c.Header.Seq
	*(*uint32)(unsafe.Pointer(&b[12:16][0])) = c.Header.Pid
	b[16] = byte(c.Body.Family)
	b[17] = byte(c.Body.Version)
	*(*uint16)(unsafe.Pointer(&b[18:20][0])) = c.Body.ResID
	return b
}

func connectNetfilter(groups uint32) (int, *syscall.SockaddrNetlink, error) {
	s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER)
	if err != nil {
		return 0, nil, err
	}
	lsa := &syscall.SockaddrNetlink{
		Family: syscall.AF_NETLINK,
		Groups: groups,
	}
	if err := syscall.Bind(s, lsa); err != nil {
		return 0, nil, err
	}

	return s, lsa, nil
}

// Make syscall asking for all connections. Invoke 'cb' for each connection.
func queryAllConnections(cb func(Conn)) error {
	s, lsa, err := connectNetfilter(0)
	if err != nil {
		return err
	}
	defer syscall.Close(s)

	msg := ConntrackListReq{
		Header: syscall.NlMsghdr{
			Len:   syscall.NLMSG_HDRLEN + sizeofGenmsg,
			Type:  (NFNL_SUBSYS_CTNETLINK << 8) | uint16(IpctnlMsgCtGet),
			Flags: syscall.NLM_F_REQUEST | syscall.NLM_F_DUMP,
			Pid:   0,
			Seq:   0,
		},
		Body: nfgenmsg{
			Family:  syscall.AF_INET,
			Version: NFNETLINK_V0,
			ResID:   0,
		},
	}
	wb := msg.toWireFormat()
	// fmt.Printf("msg bytes: %q\n", wb)
	if err := syscall.Sendto(s, wb, 0, lsa); err != nil {
		return err
	}

	return readMsgs(s, cb)
}

// Lists all the connections that conntrack is tracking.
func Connections() ([]Conn, error) {
	var conns []Conn
	queryAllConnections(func(c Conn) {
		conns = append(conns, c)
	})
	return conns, nil
}

// Established lists all established TCP connections.
func Established() ([]ConnTCP, error) {
	var conns []ConnTCP
	local := localIPs()
	err := queryAllConnections(func(c Conn) {
		if c.MsgType != NfctMsgUpdate {
			fmt.Printf("msg isn't an update: %d\n", c.MsgType)
			return
		}
		if c.TCPState != "ESTABLISHED" {
			// fmt.Printf("state isn't ESTABLISHED: %s\n", c.TCPState)
			return
		}
		if tc := c.ConnTCP(local); tc != nil {
			conns = append(conns, *tc)
		}
	})
	if err != nil {
		return nil, err
	}
	return conns, nil
}

// Follow gives a channel with all changes.
func Follow() (<-chan Conn, func(), error) {
	s, _, err := connectNetfilter(
		NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_UPDATE |
		NF_NETLINK_CONNTRACK_DESTROY)
	stop := func() {
		syscall.Close(s)
	}
	if err != nil {
		return nil, stop, err
	}

	res := make(chan Conn, 1)
	go func() {
		defer syscall.Close(s)
		if err := readMsgs(s, func(c Conn) {
			// if conn.TCPState != 3 {
			// // 3 is TCP established.
			// continue
			// }
			res <- c
		}); err != nil {
			panic(err)
		}
	}()
	return res, stop, nil
}

func readMsgs(s int, cb func(Conn)) error {
	for {
		rb := make([]byte, 2*syscall.Getpagesize()) // TODO: re-use
		nr, _, err := syscall.Recvfrom(s, rb, 0)
		if err != nil {
			return err
		}

		msgs, err := syscall.ParseNetlinkMessage(rb[:nr])
		if err != nil {
			return err
		}
		for _, msg := range msgs {
			if err := nfnlIsError(msg.Header); err != nil {
				return fmt.Errorf("msg is some error: %s\n", err)
			}
			if nflnSubsysID(msg.Header.Type) != NFNL_SUBSYS_CTNETLINK {
				return fmt.Errorf(
					"unexpected subsys_id: %d\n",
					nflnSubsysID(msg.Header.Type),
				)
			}
			conn, err := parsePayload(msg.Data[sizeofGenmsg:])
			if err != nil {
				return err
			}

			// Taken from conntrack/parse.c:__parse_message_type
			switch CntlMsgTypes(nflnMsgType(msg.Header.Type)) {
			case IpctnlMsgCtNew:
				conn.MsgType = NfctMsgUpdate
				if msg.Header.Flags&(syscall.NLM_F_CREATE|syscall.NLM_F_EXCL) > 0 {
					conn.MsgType = NfctMsgNew
				}
			case IpctnlMsgCtDelete:
				conn.MsgType = NfctMsgDestroy
			}

			cb(*conn)
		}
	}
}

type Conn struct {
	MsgType  NfConntrackMsg
	Proto    int
	Src      net.IP
	SrcPort  uint16
	Dst      net.IP
	DstPort  uint16
	TCPState string

	// ICMP stuff.
	IcmpId   uint16
	IcmpType uint8
	IcmpCode uint8

	// ct.mark, used to set permission type of the flow.
	CtMark uint32

	// ct.id, used to identify connections.
	CtId uint32

	// For multitenancy.
	Zone uint16

	// Flow stats.
	ReplyPktLen uint64
	ReplyPktCount uint64
	OrigPktLen uint64
	OrigPktCount uint64
}

// ConnTCP decides which way this connection is going and makes a ConnTCP.
func (c Conn) ConnTCP(local map[string]struct{}) *ConnTCP {
	// conntrack gives us all connections, even things passing through, but it
	// doesn't tell us what the local IP is. So we use `local` as a guide
	// what's local.
	src := c.Src.String()
	dst := c.Dst.String()
	_, srcLocal := local[src]
	_, dstLocal := local[dst]
	// If both are local we must just order things predictably.
	if srcLocal && dstLocal {
		srcLocal = c.SrcPort < c.DstPort
	}
	if srcLocal {
		return &ConnTCP{
			Local:      src,
			LocalPort:  strconv.Itoa(int(c.SrcPort)),
			Remote:     dst,
			RemotePort: strconv.Itoa(int(c.DstPort)),
		}
	}
	if dstLocal {
		return &ConnTCP{
			Local:      dst,
			LocalPort:  strconv.Itoa(int(c.DstPort)),
			Remote:     src,
			RemotePort: strconv.Itoa(int(c.SrcPort)),
		}
	}
	// Neither is local. conntrack also reports NAT connections.
	return nil
}

func parsePayload(b []byte) (*Conn, error) {
	// Most of this comes from libnetfilter_conntrack/src/conntrack/parse_mnl.c
	conn := &Conn{}
	attrs, err := parseAttrs(b)
	if err != nil {
		return conn, err
	}
	for _, attr := range attrs {
		switch CtattrType(attr.Typ) {
		case CtaTupleOrig:
		case CtaTupleReply:
			// fmt.Printf("It's a reply\n")
			// We take the reply, nor the orig.... Sure?
			parseTuple(attr.Msg, conn)
		case CtaCountersOrig:
			conn.OrigPktLen, conn.OrigPktCount, _ = parseCounters(attr.Msg)
		case CtaCountersReply:
			conn.ReplyPktLen, conn.ReplyPktCount, _ = parseCounters(attr.Msg)
		case CtaStatus:
			// These are ip_conntrack_status
			// status := binary.BigEndian.Uint32(attr.Msg)
			// fmt.Printf("It's status %d\n", status)
		case CtaProtoinfo:
			parseProtoinfo(attr.Msg, conn)
		case CtaMark:
			conn.CtMark = binary.BigEndian.Uint32(attr.Msg)
		case CtaZone:
			conn.Zone = binary.BigEndian.Uint16(attr.Msg)
		case CtaId:
			conn.CtId = binary.BigEndian.Uint32(attr.Msg)
		}
	}
	return conn, nil
}

func parseTuple(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		// fmt.Printf("pl: %d, type: %d, multi: %t, bigend: %t\n", len(attr.Msg), attr.Typ, attr.IsNested, attr.IsNetByteorder)
		switch CtattrTuple(attr.Typ) {
		case CtaTupleUnspec:
			// fmt.Printf("It's a tuple unspec\n")
		case CtaTupleIp:
			// fmt.Printf("It's a tuple IP\n")
			if err := parseIP(attr.Msg, conn); err != nil {
				return err
			}
		case CtaTupleProto:
			// fmt.Printf("It's a tuple proto\n")
			parseProto(attr.Msg, conn)
		}
	}
	return nil
}

func parseCounters(b []byte) (uint64, uint64, error) {
	attrs, err := parseAttrs(b)
	if err != nil {
		return 0, 0, fmt.Errorf("invalid tuple attr: %s", err)
	}
	packets := uint64(0)
	bytes := uint64(0)
	for _, attr := range attrs {
		switch CtattrCounters(attr.Typ) {
		case CtaCountersPackets:
			packets = binary.BigEndian.Uint64(attr.Msg)
		case CtaCountersBytes:
			bytes = binary.BigEndian.Uint64(attr.Msg)
		}
	}
	return packets, bytes, nil
}

func parseIP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrIp(attr.Typ) {
		case CtaIpV4Src:
			conn.Src = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV4Dst:
			conn.Dst = net.IP(attr.Msg) // TODO: copy so we can reuse the buffer?
		case CtaIpV6Src:
			// TODO
		case CtaIpV6Dst:
			// TODO
		}
	}
	return nil
}

func parseProto(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrL4proto(attr.Typ) {
		// Protocol number.
		case CtaProtoNum:
			conn.Proto = int(uint8(attr.Msg[0]))

		// TCP stuff.
		case CtaProtoSrcPort:
			conn.SrcPort = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoDstPort:
			conn.DstPort = binary.BigEndian.Uint16(attr.Msg)

		// ICMP stuff.
		case CtaProtoIcmpId:
			conn.IcmpId = binary.BigEndian.Uint16(attr.Msg)
		case CtaProtoIcmpType:
			conn.IcmpType = uint8(attr.Msg[0])
		case CtaProtoIcmpCode:
			conn.IcmpCode = uint8(attr.Msg[0])
		}
	}
	return nil
}

func parseProtoinfo(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfo(attr.Typ) {
		case CtaProtoinfoTcp:
			if err := parseProtoinfoTCP(attr.Msg, conn); err != nil {
				return err
			}
		default:
			// we're not interested in other protocols
		}
	}
	return nil
}

func parseProtoinfoTCP(b []byte, conn *Conn) error {
	attrs, err := parseAttrs(b)
	if err != nil {
		return fmt.Errorf("invalid tuple attr: %s", err)
	}
	for _, attr := range attrs {
		switch CtattrProtoinfoTcp(attr.Typ) {
		case CtaProtoinfoTcpState:
			conn.TCPState = tcpState[uint8(attr.Msg[0])]
		default:
			// not interested
		}
	}
	return nil
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Red Hat, Inc.
//
// 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.
//
// Red Hat trademarks are not licensed under Apache License, Version 2.
// No permission is granted to use or replicate Red Hat trademarks that
// are incorporated in this software or its documentation.
//

package adapters

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	logging "github.com/op/go-logging"
	"github.com/openshift/ansible-service-broker/pkg/apb"
)

// RHCCAdapter - Red Hat Container Catalog Registry
type RHCCAdapter struct {
	Config Configuration
	Log    *logging.Logger
}

// RHCCImage - RHCC Registry Image that is returned from the RHCC Catalog api.
type RHCCImage struct {
	Description  string `json:"description"`
	IsOfficial   bool   `json:"is_official"`
	IsTrusted    bool   `json:"is_trusted"`
	Name         string `json:"name"`
	ShouldFilter bool   `json:"should_filter"`
	StarCount    int    `json:"star_count"`
}

// RHCCImageResponse - RHCC Registry Image Response returned for the RHCC Catalog api
type RHCCImageResponse struct {
	NumResults int          `json:"num_results"`
	Query      string       `json:"query"`
	Results    []*RHCCImage `json:"results"`
}

// RegistryName - retrieve the registry prefix
func (r RHCCAdapter) RegistryName() string {
	return r.Config.URL.Host
}

// GetImageNames - retrieve the images from the registry
func (r RHCCAdapter) GetImageNames() ([]string, error) {
	imageList, err := r.loadImages("\"*-apb\"")
	if err != nil {
		return nil, err
	}
	imageNames := []string{}
	for _, image := range imageList.Results {
		imageNames = append(imageNames, image.Name)
	}
	return imageNames, nil
}

// FetchSpecs - retrieve the spec from the image names
func (r RHCCAdapter) FetchSpecs(imageNames []string) ([]*apb.Spec, error) {
	specs := []*apb.Spec{}
	if r.Config.Tag == "" {
		r.Config.Tag = "latest"
	}
	for _, imageName := range imageNames {
		req, err := http.NewRequest("GET",
			fmt.Sprintf("%v/v2/%v/manifests/%v", r.Config.URL.String(), imageName, r.Config.Tag), nil)
		if err != nil {
			return specs, err
		}
		spec, err := imageToSpec(r.Log, req, fmt.Sprintf("%s/%s:%s", r.RegistryName(), imageName, r.Config.Tag))
		if err != nil {
			return specs, err
		}
		if spec != nil {
			specs = append(specs, spec)
		}
	}
	return specs, nil
}

// LoadImages - Get all the images for a particular query
func (r RHCCAdapter) loadImages(Query string) (RHCCImageResponse, error) {
	r.Log.Debug("RHCCRegistry::LoadImages")
	r.Log.Debug("Using " + r.Config.URL.String() + " to source APB images using query:" + Query)
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%v/v1/search?q=%v", r.Config.URL.String(), Query), nil)
	if err != nil {
		return RHCCImageResponse{}, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return RHCCImageResponse{}, err
	}
	defer resp.Body.Close()

	r.Log.Debug("Got Image Response from RHCC")
	imageList, err := ioutil.ReadAll(resp.Body)

	imageResp := RHCCImageResponse{}
	err = json.Unmarshal(imageList, &imageResp)
	if err != nil {
		return RHCCImageResponse{}, err
	}
	r.Log.Debug("Properly unmarshalled image response")

	return imageResp, nil
}
<commit_msg>Look for the url in the proper place (#535)<commit_after>//
// Copyright (c) 2017 Red Hat, Inc.
//
// 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.
//
// Red Hat trademarks are not licensed under Apache License, Version 2.
// No permission is granted to use or replicate Red Hat trademarks that
// are incorporated in this software or its documentation.
//

package adapters

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	logging "github.com/op/go-logging"
	"github.com/openshift/ansible-service-broker/pkg/apb"
)

// RHCCAdapter - Red Hat Container Catalog Registry
type RHCCAdapter struct {
	Config Configuration
	Log    *logging.Logger
}

// RHCCImage - RHCC Registry Image that is returned from the RHCC Catalog api.
type RHCCImage struct {
	Description  string `json:"description"`
	IsOfficial   bool   `json:"is_official"`
	IsTrusted    bool   `json:"is_trusted"`
	Name         string `json:"name"`
	ShouldFilter bool   `json:"should_filter"`
	StarCount    int    `json:"star_count"`
}

// RHCCImageResponse - RHCC Registry Image Response returned for the RHCC Catalog api
type RHCCImageResponse struct {
	NumResults int          `json:"num_results"`
	Query      string       `json:"query"`
	Results    []*RHCCImage `json:"results"`
}

// RegistryName - retrieve the registry prefix
func (r RHCCAdapter) RegistryName() string {
	return r.Config.URL.String()
}

// GetImageNames - retrieve the images from the registry
func (r RHCCAdapter) GetImageNames() ([]string, error) {
	imageList, err := r.loadImages("\"*-apb\"")
	if err != nil {
		return nil, err
	}
	imageNames := []string{}
	for _, image := range imageList.Results {
		imageNames = append(imageNames, image.Name)
	}
	return imageNames, nil
}

// FetchSpecs - retrieve the spec from the image names
func (r RHCCAdapter) FetchSpecs(imageNames []string) ([]*apb.Spec, error) {
	specs := []*apb.Spec{}
	if r.Config.Tag == "" {
		r.Config.Tag = "latest"
	}
	for _, imageName := range imageNames {
		req, err := http.NewRequest("GET",
			fmt.Sprintf("%v/v2/%v/manifests/%v", r.Config.URL.String(), imageName, r.Config.Tag), nil)
		if err != nil {
			return specs, err
		}
		spec, err := imageToSpec(r.Log, req, fmt.Sprintf("%s/%s:%s", r.RegistryName(), imageName, r.Config.Tag))
		if err != nil {
			return specs, err
		}
		if spec != nil {
			specs = append(specs, spec)
		}
	}
	return specs, nil
}

// LoadImages - Get all the images for a particular query
func (r RHCCAdapter) loadImages(Query string) (RHCCImageResponse, error) {
	r.Log.Debug("RHCCRegistry::LoadImages")
	r.Log.Debug("Using " + r.Config.URL.String() + " to source APB images using query:" + Query)
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%v/v1/search?q=%v", r.Config.URL.String(), Query), nil)
	if err != nil {
		return RHCCImageResponse{}, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return RHCCImageResponse{}, err
	}
	defer resp.Body.Close()

	r.Log.Debug("Got Image Response from RHCC")
	imageList, err := ioutil.ReadAll(resp.Body)

	imageResp := RHCCImageResponse{}
	err = json.Unmarshal(imageList, &imageResp)
	if err != nil {
		return RHCCImageResponse{}, err
	}
	r.Log.Debug("Properly unmarshalled image response")

	return imageResp, nil
}
<|endoftext|>


<commit_before>package vitali

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

func (c *webApp) marshalOutput(w *wrappedWriter, model *interface{}, ctx *Ctx, templateName string) {
    switch ctx.ChosenType {
    case "application/json":
        fmt.Fprintf(w, "%s", string(panicOnErr(json.Marshal(model)).([]byte)))
    case "application/xml":
        fmt.Fprintf(w, "%s", string(panicOnErr(xml.Marshal(model)).([]byte)))
    case "text/html":
        m := struct{
            S map[string]template.HTML
            M *interface{}
            C *Ctx
        }{
            c.I18n[ctx.ChosenLang],
            model,
            ctx,
        }
        c.views[templateName].Execute(w, m)
    default:
        fmt.Fprintf(w, "%s", model)
    }
}

func (c *webApp) writeResponse(w *wrappedWriter, r *http.Request, response *interface{}, ctx *Ctx, templateName string) {
    switch v := (*response).(type) {
    case noContent:
        w.WriteHeader(http.StatusNoContent)
    case view:
        w.Header().Set("Content-Type", "text/html")
        w.WriteHeader(http.StatusOK)
        v.template.Execute(w, v.model)
    case movedPermanently:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusMovedPermanently)
        fmt.Fprintf(w, "%s\n", v.uri)
    case found:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusFound)
        fmt.Fprintf(w, "%s\n", v.uri)
    case seeOther:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusSeeOther)
        fmt.Fprintf(w, "%s\n", v.uri)
    case tempRedirect:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusTemporaryRedirect)
    case badRequest:
        http.Error(w, v.reason, http.StatusBadRequest)
    case unauthorized:
        w.Header()["WWW-Authenticate"] = []string{v.wwwAuthHeader}
        if c.Settings["401_PAGE"] != "" && ctx.ChosenType == "text/html" {
            w.Header().Set("Content-Type", "text/html")
            w.WriteHeader(http.StatusUnauthorized)
            f, err := os.Open(c.Settings["401_PAGE"])
            if err != nil {
                log.Printf("open 401 page error: %s\n", err)
                return
            }
            defer f.Close()
            io.Copy(w, f)
        } else {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
        }
    case forbidden:
        http.Error(w, "Forbidden", http.StatusForbidden)
    case notFound:
        http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    case methodNotAllowed:
        w.Header().Set("Allow", strings.Join(v.allowed, ", "))
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed),
            http.StatusMethodNotAllowed)
    case notAcceptable:
        w.Header().Set("Content-Type", "text/csv")
        types := make([]string, len(v.provided))
        for i, mediaType := range(v.provided){
            types[i] = string(mediaType)
        }
        http.Error(w, strings.Join(([]string)(types), ","),
            http.StatusNotAcceptable)
    case unsupportedMediaType:
        http.Error(w, http.StatusText(http.StatusUnsupportedMediaType),
            http.StatusUnsupportedMediaType)
    case internalError:
        w.err = v
        if c.ErrTemplate != nil {
            w.WriteHeader(http.StatusInternalServerError)
            md := struct {Code uint32}{w.err.code}
            c.ErrTemplate.Execute(w, md)
        } else {
            http.Error(w, fmt.Sprintf("%s: %d", http.StatusText(http.StatusInternalServerError),
                w.err.code), http.StatusInternalServerError)
        }
    case notImplemented:
        http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
    case serviceUnavailable:
        if v.seconds >= 0 {
            w.Header().Set("Retry-After", fmt.Sprintf("%d", v.seconds))
        }
        http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
    case error:
        w.err = internalError{
            where: "",
            why: v.Error(),
            code: errorCode(v.Error()),
        }
        http.Error(w, fmt.Sprintf("%s: %d", http.StatusText(http.StatusInternalServerError),
            w.err.code), http.StatusInternalServerError)
    case io.ReadCloser:
        defer v.Close()
        if r.Header.Get("Range") != "" {
            w.WriteHeader(http.StatusPartialContent)
        } else {
            w.WriteHeader(http.StatusOK)
        }
        io.Copy(w, v)
    case http.ResponseWriter:
    case clientGone:
    default:
        if r.Header.Get("Range") != "" {
            w.WriteHeader(http.StatusPartialContent)
        } else {
            w.WriteHeader(http.StatusOK)
        }
        if ctx.ChosenType != "" {
            c.marshalOutput(w, &v, ctx, templateName)
        } else {
            fmt.Fprintf(w, "%s", v)
        }
    }
}
<commit_msg>fix default output serialize<commit_after>package vitali

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

func (c *webApp) marshalOutput(w *wrappedWriter, model *interface{}, ctx *Ctx, templateName string) {
    switch ctx.ChosenType {
    case "application/json":
        fmt.Fprintf(w, "%s", string(panicOnErr(json.Marshal(model)).([]byte)))
    case "application/xml":
        fmt.Fprintf(w, "%s", string(panicOnErr(xml.Marshal(model)).([]byte)))
    case "text/html":
        m := struct{
            S map[string]template.HTML
            M *interface{}
            C *Ctx
        }{
            c.I18n[ctx.ChosenLang],
            model,
            ctx,
        }
        c.views[templateName].Execute(w, m)
    default:
        fmt.Fprintf(w, "%s", *model)
    }
}

func (c *webApp) writeResponse(w *wrappedWriter, r *http.Request, response *interface{}, ctx *Ctx, templateName string) {
    switch v := (*response).(type) {
    case noContent:
        w.WriteHeader(http.StatusNoContent)
    case view:
        w.Header().Set("Content-Type", "text/html")
        w.WriteHeader(http.StatusOK)
        v.template.Execute(w, v.model)
    case movedPermanently:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusMovedPermanently)
        fmt.Fprintf(w, "%s\n", v.uri)
    case found:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusFound)
        fmt.Fprintf(w, "%s\n", v.uri)
    case seeOther:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusSeeOther)
        fmt.Fprintf(w, "%s\n", v.uri)
    case tempRedirect:
        w.Header().Set("Location", v.uri)
        w.WriteHeader(http.StatusTemporaryRedirect)
    case badRequest:
        http.Error(w, v.reason, http.StatusBadRequest)
    case unauthorized:
        w.Header()["WWW-Authenticate"] = []string{v.wwwAuthHeader}
        if c.Settings["401_PAGE"] != "" && ctx.ChosenType == "text/html" {
            w.Header().Set("Content-Type", "text/html")
            w.WriteHeader(http.StatusUnauthorized)
            f, err := os.Open(c.Settings["401_PAGE"])
            if err != nil {
                log.Printf("open 401 page error: %s\n", err)
                return
            }
            defer f.Close()
            io.Copy(w, f)
        } else {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
        }
    case forbidden:
        http.Error(w, "Forbidden", http.StatusForbidden)
    case notFound:
        http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
    case methodNotAllowed:
        w.Header().Set("Allow", strings.Join(v.allowed, ", "))
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed),
            http.StatusMethodNotAllowed)
    case notAcceptable:
        w.Header().Set("Content-Type", "text/csv")
        types := make([]string, len(v.provided))
        for i, mediaType := range(v.provided){
            types[i] = string(mediaType)
        }
        http.Error(w, strings.Join(([]string)(types), ","),
            http.StatusNotAcceptable)
    case unsupportedMediaType:
        http.Error(w, http.StatusText(http.StatusUnsupportedMediaType),
            http.StatusUnsupportedMediaType)
    case internalError:
        w.err = v
        if c.ErrTemplate != nil {
            w.WriteHeader(http.StatusInternalServerError)
            md := struct {Code uint32}{w.err.code}
            c.ErrTemplate.Execute(w, md)
        } else {
            http.Error(w, fmt.Sprintf("%s: %d", http.StatusText(http.StatusInternalServerError),
                w.err.code), http.StatusInternalServerError)
        }
    case notImplemented:
        http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
    case serviceUnavailable:
        if v.seconds >= 0 {
            w.Header().Set("Retry-After", fmt.Sprintf("%d", v.seconds))
        }
        http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
    case error:
        w.err = internalError{
            where: "",
            why: v.Error(),
            code: errorCode(v.Error()),
        }
        http.Error(w, fmt.Sprintf("%s: %d", http.StatusText(http.StatusInternalServerError),
            w.err.code), http.StatusInternalServerError)
    case io.ReadCloser:
        defer v.Close()
        if r.Header.Get("Range") != "" {
            w.WriteHeader(http.StatusPartialContent)
        } else {
            w.WriteHeader(http.StatusOK)
        }
        io.Copy(w, v)
    case http.ResponseWriter:
    case clientGone:
    default:
        if r.Header.Get("Range") != "" {
            w.WriteHeader(http.StatusPartialContent)
        } else {
            w.WriteHeader(http.StatusOK)
        }
        if ctx.ChosenType != "" {
            c.marshalOutput(w, &v, ctx, templateName)
        } else {
            fmt.Fprintf(w, "%s", v)
        }
    }
}
<|endoftext|>


<commit_before>// +build debug

package bitcoindnotify

import (
	"io/ioutil"
	"testing"
	"time"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/integration/rpctest"
	"github.com/btcsuite/btcwallet/chain"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/channeldb"
)

func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
	t.Helper()

	tempDir, err := ioutil.TempDir("", "kek")
	if err != nil {
		t.Fatalf("unable to create temp dir: %v", err)
	}
	db, err := channeldb.Open(tempDir)
	if err != nil {
		t.Fatalf("unable to create db: %v", err)
	}
	hintCache, err := chainntnfs.NewHeightHintCache(db)
	if err != nil {
		t.Fatalf("unable to create hint cache: %v", err)
	}

	return hintCache
}

// setUpNotifier is a helper function to start a new notifier backed by a
// bitcoind driver.
func setUpNotifier(t *testing.T, bitcoindConn *chain.BitcoindConn,
	spendHintCache chainntnfs.SpendHintCache,
	confirmHintCache chainntnfs.ConfirmHintCache) *BitcoindNotifier {

	t.Helper()

	notifier := New(bitcoindConn, spendHintCache, confirmHintCache)
	if err := notifier.Start(); err != nil {
		t.Fatalf("unable to start notifier: %v", err)
	}

	return notifier
}

// syncNotifierWithMiner is a helper method that attempts to wait until the
// notifier is synced (in terms of the chain) with the miner.
func syncNotifierWithMiner(t *testing.T, notifier *BitcoindNotifier,
	miner *rpctest.Harness) uint32 {

	t.Helper()

	_, minerHeight, err := miner.Node.GetBestBlock()
	if err != nil {
		t.Fatalf("unable to retrieve miner's current height: %v", err)
	}

	timeout := time.After(10 * time.Second)
	for {
		_, bitcoindHeight, err := notifier.chainConn.GetBestBlock()
		if err != nil {
			t.Fatalf("unable to retrieve bitcoind's current "+
				"height: %v", err)
		}

		if bitcoindHeight == minerHeight {
			return uint32(bitcoindHeight)
		}

		select {
		case <-time.After(100 * time.Millisecond):
		case <-timeout:
			t.Fatalf("timed out waiting to sync notifier")
		}
	}
}

// TestHistoricalConfDetailsTxIndex ensures that we correctly retrieve
// historical confirmation details using the backend node's txindex.
func TestHistoricalConfDetailsTxIndex(t *testing.T) {
	miner, tearDown := chainntnfs.NewMiner(
		t, []string{"--txindex"}, true, 25,
	)
	defer tearDown()

	bitcoindConn, cleanUp := chainntnfs.NewBitcoindBackend(
		t, miner.P2PAddress(), true,
	)
	defer cleanUp()

	hintCache := initHintCache(t)

	notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
	defer notifier.Stop()

	syncNotifierWithMiner(t, notifier, miner)

	// A transaction unknown to the node should not be found within the
	// txindex even if it is enabled, so we should not proceed with any
	// fallback methods.
	var zeroHash chainhash.Hash
	_, txStatus, err := notifier.historicalConfDetails(&zeroHash, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	switch txStatus {
	case chainntnfs.TxNotFoundIndex:
	case chainntnfs.TxNotFoundManually:
		t.Fatal("should not have proceeded with fallback method, but did")
	default:
		t.Fatal("should not have found non-existent transaction, but did")
	}

	// Now, we'll create a test transaction, confirm it, and attempt to
	// retrieve its confirmation details.
	txid, _, err := chainntnfs.GetTestTxidAndScript(miner)
	if err != nil {
		t.Fatalf("unable to create tx: %v", err)
	}
	if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
		t.Fatal(err)
	}

	// The transaction should be found in the mempool at this point.
	_, txStatus, err = notifier.historicalConfDetails(txid, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since it has yet to be included in a block, it should have been found
	// within the mempool.
	switch txStatus {
	case chainntnfs.TxFoundMempool:
	default:
		t.Fatal("should have found the transaction within the "+
			"mempool, but did not: %v", txStatus)
	}

	if _, err := miner.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}

	// Ensure the notifier and miner are synced to the same height to ensure
	// the txindex includes the transaction just mined.
	syncNotifierWithMiner(t, notifier, miner)

	_, txStatus, err = notifier.historicalConfDetails(txid, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since the backend node's txindex is enabled and the transaction has
	// confirmed, we should be able to retrieve it using the txindex.
	switch txStatus {
	case chainntnfs.TxFoundIndex:
	default:
		t.Fatal("should have found the transaction within the " +
			"txindex, but did not")
	}
}

// TestHistoricalConfDetailsNoTxIndex ensures that we correctly retrieve
// historical confirmation details using the set of fallback methods when the
// backend node's txindex is disabled.
func TestHistoricalConfDetailsNoTxIndex(t *testing.T) {
	miner, tearDown := chainntnfs.NewMiner(t, nil, true, 25)
	defer tearDown()

	bitcoindConn, cleanUp := chainntnfs.NewBitcoindBackend(
		t, miner.P2PAddress(), false,
	)
	defer cleanUp()

	hintCache := initHintCache(t)

	notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
	defer notifier.Stop()

	// Since the node has its txindex disabled, we fall back to scanning the
	// chain manually. A transaction unknown to the network should not be
	// found.
	var zeroHash chainhash.Hash
	broadcastHeight := syncNotifierWithMiner(t, notifier, miner)
	_, txStatus, err := notifier.historicalConfDetails(
		&zeroHash, uint32(broadcastHeight), uint32(broadcastHeight),
	)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	switch txStatus {
	case chainntnfs.TxNotFoundManually:
	case chainntnfs.TxNotFoundIndex:
		t.Fatal("should have proceeded with fallback method, but did not")
	default:
		t.Fatal("should not have found non-existent transaction, but did")
	}

	// Now, we'll create a test transaction and attempt to retrieve its
	// confirmation details. In order to fall back to manually scanning the
	// chain, the transaction must be in the chain and not contain any
	// unspent outputs. To ensure this, we'll create a transaction with only
	// one output, which we will manually spend. The backend node's
	// transaction index should also be disabled, which we've already
	// ensured above.
	output, pkScript := chainntnfs.CreateSpendableOutput(t, miner)
	spendTx := chainntnfs.CreateSpendTx(t, output, pkScript)
	spendTxHash, err := miner.Node.SendRawTransaction(spendTx, true)
	if err != nil {
		t.Fatalf("unable to broadcast tx: %v", err)
	}
	if err := chainntnfs.WaitForMempoolTx(miner, spendTxHash); err != nil {
		t.Fatalf("tx not relayed to miner: %v", err)
	}
	if _, err := miner.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}

	// Ensure the notifier and miner are synced to the same height to ensure
	// we can find the transaction when manually scanning the chain.
	currentHeight := syncNotifierWithMiner(t, notifier, miner)
	_, txStatus, err = notifier.historicalConfDetails(
		&output.Hash, uint32(broadcastHeight), uint32(currentHeight),
	)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since the backend node's txindex is disabled and the transaction has
	// confirmed, we should be able to find it by falling back to scanning
	// the chain manually.
	switch txStatus {
	case chainntnfs.TxFoundManually:
	default:
		t.Fatal("should have found the transaction by manually " +
			"scanning the chain, but did not")
	}
}
<commit_msg>chainntnfs/bitcoindnotify: disable height hints in testing<commit_after>// +build debug

package bitcoindnotify

import (
	"io/ioutil"
	"testing"
	"time"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/integration/rpctest"
	"github.com/btcsuite/btcwallet/chain"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/channeldb"
)

func initHintCache(t *testing.T) *chainntnfs.HeightHintCache {
	t.Helper()

	tempDir, err := ioutil.TempDir("", "kek")
	if err != nil {
		t.Fatalf("unable to create temp dir: %v", err)
	}
	db, err := channeldb.Open(tempDir)
	if err != nil {
		t.Fatalf("unable to create db: %v", err)
	}
	hintCache, err := chainntnfs.NewHeightHintCache(db, true)
	if err != nil {
		t.Fatalf("unable to create hint cache: %v", err)
	}

	return hintCache
}

// setUpNotifier is a helper function to start a new notifier backed by a
// bitcoind driver.
func setUpNotifier(t *testing.T, bitcoindConn *chain.BitcoindConn,
	spendHintCache chainntnfs.SpendHintCache,
	confirmHintCache chainntnfs.ConfirmHintCache) *BitcoindNotifier {

	t.Helper()

	notifier := New(bitcoindConn, spendHintCache, confirmHintCache)
	if err := notifier.Start(); err != nil {
		t.Fatalf("unable to start notifier: %v", err)
	}

	return notifier
}

// syncNotifierWithMiner is a helper method that attempts to wait until the
// notifier is synced (in terms of the chain) with the miner.
func syncNotifierWithMiner(t *testing.T, notifier *BitcoindNotifier,
	miner *rpctest.Harness) uint32 {

	t.Helper()

	_, minerHeight, err := miner.Node.GetBestBlock()
	if err != nil {
		t.Fatalf("unable to retrieve miner's current height: %v", err)
	}

	timeout := time.After(10 * time.Second)
	for {
		_, bitcoindHeight, err := notifier.chainConn.GetBestBlock()
		if err != nil {
			t.Fatalf("unable to retrieve bitcoind's current "+
				"height: %v", err)
		}

		if bitcoindHeight == minerHeight {
			return uint32(bitcoindHeight)
		}

		select {
		case <-time.After(100 * time.Millisecond):
		case <-timeout:
			t.Fatalf("timed out waiting to sync notifier")
		}
	}
}

// TestHistoricalConfDetailsTxIndex ensures that we correctly retrieve
// historical confirmation details using the backend node's txindex.
func TestHistoricalConfDetailsTxIndex(t *testing.T) {
	miner, tearDown := chainntnfs.NewMiner(
		t, []string{"--txindex"}, true, 25,
	)
	defer tearDown()

	bitcoindConn, cleanUp := chainntnfs.NewBitcoindBackend(
		t, miner.P2PAddress(), true,
	)
	defer cleanUp()

	hintCache := initHintCache(t)

	notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
	defer notifier.Stop()

	syncNotifierWithMiner(t, notifier, miner)

	// A transaction unknown to the node should not be found within the
	// txindex even if it is enabled, so we should not proceed with any
	// fallback methods.
	var zeroHash chainhash.Hash
	_, txStatus, err := notifier.historicalConfDetails(&zeroHash, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	switch txStatus {
	case chainntnfs.TxNotFoundIndex:
	case chainntnfs.TxNotFoundManually:
		t.Fatal("should not have proceeded with fallback method, but did")
	default:
		t.Fatal("should not have found non-existent transaction, but did")
	}

	// Now, we'll create a test transaction, confirm it, and attempt to
	// retrieve its confirmation details.
	txid, _, err := chainntnfs.GetTestTxidAndScript(miner)
	if err != nil {
		t.Fatalf("unable to create tx: %v", err)
	}
	if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
		t.Fatal(err)
	}

	// The transaction should be found in the mempool at this point.
	_, txStatus, err = notifier.historicalConfDetails(txid, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since it has yet to be included in a block, it should have been found
	// within the mempool.
	switch txStatus {
	case chainntnfs.TxFoundMempool:
	default:
		t.Fatal("should have found the transaction within the "+
			"mempool, but did not: %v", txStatus)
	}

	if _, err := miner.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}

	// Ensure the notifier and miner are synced to the same height to ensure
	// the txindex includes the transaction just mined.
	syncNotifierWithMiner(t, notifier, miner)

	_, txStatus, err = notifier.historicalConfDetails(txid, 0, 0)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since the backend node's txindex is enabled and the transaction has
	// confirmed, we should be able to retrieve it using the txindex.
	switch txStatus {
	case chainntnfs.TxFoundIndex:
	default:
		t.Fatal("should have found the transaction within the " +
			"txindex, but did not")
	}
}

// TestHistoricalConfDetailsNoTxIndex ensures that we correctly retrieve
// historical confirmation details using the set of fallback methods when the
// backend node's txindex is disabled.
func TestHistoricalConfDetailsNoTxIndex(t *testing.T) {
	miner, tearDown := chainntnfs.NewMiner(t, nil, true, 25)
	defer tearDown()

	bitcoindConn, cleanUp := chainntnfs.NewBitcoindBackend(
		t, miner.P2PAddress(), false,
	)
	defer cleanUp()

	hintCache := initHintCache(t)

	notifier := setUpNotifier(t, bitcoindConn, hintCache, hintCache)
	defer notifier.Stop()

	// Since the node has its txindex disabled, we fall back to scanning the
	// chain manually. A transaction unknown to the network should not be
	// found.
	var zeroHash chainhash.Hash
	broadcastHeight := syncNotifierWithMiner(t, notifier, miner)
	_, txStatus, err := notifier.historicalConfDetails(
		&zeroHash, uint32(broadcastHeight), uint32(broadcastHeight),
	)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	switch txStatus {
	case chainntnfs.TxNotFoundManually:
	case chainntnfs.TxNotFoundIndex:
		t.Fatal("should have proceeded with fallback method, but did not")
	default:
		t.Fatal("should not have found non-existent transaction, but did")
	}

	// Now, we'll create a test transaction and attempt to retrieve its
	// confirmation details. In order to fall back to manually scanning the
	// chain, the transaction must be in the chain and not contain any
	// unspent outputs. To ensure this, we'll create a transaction with only
	// one output, which we will manually spend. The backend node's
	// transaction index should also be disabled, which we've already
	// ensured above.
	output, pkScript := chainntnfs.CreateSpendableOutput(t, miner)
	spendTx := chainntnfs.CreateSpendTx(t, output, pkScript)
	spendTxHash, err := miner.Node.SendRawTransaction(spendTx, true)
	if err != nil {
		t.Fatalf("unable to broadcast tx: %v", err)
	}
	if err := chainntnfs.WaitForMempoolTx(miner, spendTxHash); err != nil {
		t.Fatalf("tx not relayed to miner: %v", err)
	}
	if _, err := miner.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}

	// Ensure the notifier and miner are synced to the same height to ensure
	// we can find the transaction when manually scanning the chain.
	currentHeight := syncNotifierWithMiner(t, notifier, miner)
	_, txStatus, err = notifier.historicalConfDetails(
		&output.Hash, uint32(broadcastHeight), uint32(currentHeight),
	)
	if err != nil {
		t.Fatalf("unable to retrieve historical conf details: %v", err)
	}

	// Since the backend node's txindex is disabled and the transaction has
	// confirmed, we should be able to find it by falling back to scanning
	// the chain manually.
	switch txStatus {
	case chainntnfs.TxFoundManually:
	default:
		t.Fatal("should have found the transaction by manually " +
			"scanning the chain, but did not")
	}
}
<|endoftext|>


<commit_before>package rpm

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/dtylman/gopack/files"
)

const (
	AMD64 = "x86_64"
)

type Rpm struct {
	Spec          *SpecFile
	workingFolder string
	buildRoot     string
}

func New(name, version, revision, arch string) (*Rpm, error) {
	r := new(Rpm)
	r.Spec = newSpec()

	var err error
	r.workingFolder, err = ioutil.TempDir("", name)
	if err != nil {
		return nil, err
	}
	r.Spec.SetName(name)
	r.Spec.SetVersion(version, revision)
	r.buildRoot = filepath.Join(r.workingFolder, "BUILD")
	err = os.MkdirAll(r.buildRoot, 0755)
	if err != nil {
		os.RemoveAll(r.workingFolder)
		return nil, err
	}
	r.Spec.Header[BuildRoot] = r.buildRoot
	r.Spec.Header[BuildArch] = arch
	r.Spec.AddDefine("_tmppath " + os.TempDir())
	r.Spec.AddDefine("_topdir " + r.workingFolder)
	r.Spec.AddDefine("_sourcedir " + r.workingFolder)
	r.Spec.AddDefine("buildroot " + r.buildRoot)
	r.Spec.AddDefine("_target_os linux")
	// Disable the stupid stuff rpm distros include in the build process by default:
	// Disable any prep shell actions. replace them with simply 'true'
	r.Spec.AddDefine("__spec_prep_post true")
	r.Spec.AddDefine("__spec_prep_pre true")
	r.Spec.AddDefine("__spec_build_post true")
	r.Spec.AddDefine("__spec_build_pre true")
	r.Spec.AddDefine("__spec_install_post true")
	r.Spec.AddDefine("__spec_install_pre true")
	r.Spec.AddDefine("__spec_clean_post true")
	r.Spec.AddDefine("__spec_clean_pre true")

	return r, nil
}

func (r *Rpm) Close() error {
	return os.RemoveAll(r.workingFolder)
}

/*

	//Running rpmbuild {:args=>["rpmbuild", "-bb", "--define", "buildroot /tmp/package-rpm-build20160419-10869-1byo5sr/BUILD", "--define", "_topdir /tmp/package-rpm-build20160419-10869-1byo5sr",
	// "--define", "_sourcedir /tmp/package-rpm-build20160419-10869-1byo5sr", "--define", "_rpmdir /tmp/package-rpm-build20160419-10869-1byo5sr/RPMS", "--define", "_tmppath /tmp",
	// "/tmp/package-rpm-build20160419-10869-1byo5sr/SPECS/demistoserver.spec"], :level=>:info}

*/
func (r *Rpm) Create(folder string) (string, error) {
	rpmdir, err := filepath.Abs(folder)
	if err != nil {
		return "", err
	}
	r.Spec.AddDefine("_rpmdir " + rpmdir)
	specFolder := filepath.Join(r.workingFolder, "SPECS")
	err = os.MkdirAll(specFolder, 0755)
	if err != nil {
		return "", err
	}
	pkgName := r.Spec.Header[PkgName]

	specFile, err := os.Create(filepath.Join(specFolder, pkgName+".spec"))
	if err != nil {
		return "", err
	}
	defer specFile.Close()
	err = r.Spec.Write(specFile)
	if err != nil {
		return "", err
	}
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("rpmbuild", "-bb", specFile.Name())
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("rpmbuild failed with: %v. Stdout: %v. Stderr: %v", err, stdout.String(), stderr.String())
	}
	rpmFile := filepath.Join(rpmdir, r.Spec.Header[BuildArch], r.Spec.PackageName())
	_, err = os.Stat(rpmFile)
	if err != nil {
		return "", err
	}
	return rpmFile, nil
}

func (r *Rpm) movePackageFile(path string, info os.FileInfo, err error) error {
	if !info.IsDir() {
		if strings.HasSuffix(strings.ToLower(path), ".rpm") {
			return os.Rename(path, filepath.Base(path))
		}
	}
	return nil
}

func (r *Rpm) AddEmptyFolder(name string) error {
	destFolder := filepath.Join(r.buildRoot, name)
	err := os.MkdirAll(destFolder, 0755)
	if err != nil {
		return err
	}
	r.Spec.AddFile(name)
	return nil
}

func (r *Rpm) AddFolder(path string, prefix string) error {
	fc, err := files.New(path)
	if err != nil {
		return err
	}
	baseDir := filepath.Dir(path)
	for _, path := range fc.Files {
		targetPath := filepath.Join(prefix, strings.TrimPrefix(path, baseDir))
		err = r.AddFile(path, targetPath)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *Rpm) AddFile(sourcePath string, targetPath string) error {
	srcFile, err := os.Open(sourcePath)
	if err != nil {
		return err
	}
	defer srcFile.Close()
	destPath := filepath.Join(r.buildRoot, targetPath)
	err = os.MkdirAll(filepath.Dir(destPath), 0755)
	if err != nil {
		return err
	}
	destFile, err := os.Create(destPath)
	if err != nil {
		return err
	}
	defer destFile.Close()
	_, err = io.Copy(destFile, srcFile)
	if err != nil {
		return err
	}
	r.Spec.AddFile(targetPath)
	return nil
}
<commit_msg>preserve the file permission when adding files to rpm<commit_after>package rpm

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/dtylman/gopack/files"
)

const (
	AMD64 = "x86_64"
)

type Rpm struct {
	Spec          *SpecFile
	workingFolder string
	buildRoot     string
}

func New(name, version, revision, arch string) (*Rpm, error) {
	r := new(Rpm)
	r.Spec = newSpec()

	var err error
	r.workingFolder, err = ioutil.TempDir("", name)
	if err != nil {
		return nil, err
	}
	r.Spec.SetName(name)
	r.Spec.SetVersion(version, revision)
	r.buildRoot = filepath.Join(r.workingFolder, "BUILD")
	err = os.MkdirAll(r.buildRoot, 0755)
	if err != nil {
		os.RemoveAll(r.workingFolder)
		return nil, err
	}
	r.Spec.Header[BuildRoot] = r.buildRoot
	r.Spec.Header[BuildArch] = arch
	r.Spec.AddDefine("_tmppath " + os.TempDir())
	r.Spec.AddDefine("_topdir " + r.workingFolder)
	r.Spec.AddDefine("_sourcedir " + r.workingFolder)
	r.Spec.AddDefine("buildroot " + r.buildRoot)
	r.Spec.AddDefine("_target_os linux")
	// Disable the stupid stuff rpm distros include in the build process by default:
	// Disable any prep shell actions. replace them with simply 'true'
	r.Spec.AddDefine("__spec_prep_post true")
	r.Spec.AddDefine("__spec_prep_pre true")
	r.Spec.AddDefine("__spec_build_post true")
	r.Spec.AddDefine("__spec_build_pre true")
	r.Spec.AddDefine("__spec_install_post true")
	r.Spec.AddDefine("__spec_install_pre true")
	r.Spec.AddDefine("__spec_clean_post true")
	r.Spec.AddDefine("__spec_clean_pre true")

	return r, nil
}

func (r *Rpm) Close() error {
	return os.RemoveAll(r.workingFolder)
}

/*

	//Running rpmbuild {:args=>["rpmbuild", "-bb", "--define", "buildroot /tmp/package-rpm-build20160419-10869-1byo5sr/BUILD", "--define", "_topdir /tmp/package-rpm-build20160419-10869-1byo5sr",
	// "--define", "_sourcedir /tmp/package-rpm-build20160419-10869-1byo5sr", "--define", "_rpmdir /tmp/package-rpm-build20160419-10869-1byo5sr/RPMS", "--define", "_tmppath /tmp",
	// "/tmp/package-rpm-build20160419-10869-1byo5sr/SPECS/demistoserver.spec"], :level=>:info}

*/
func (r *Rpm) Create(folder string) (string, error) {
	rpmdir, err := filepath.Abs(folder)
	if err != nil {
		return "", err
	}
	r.Spec.AddDefine("_rpmdir " + rpmdir)
	specFolder := filepath.Join(r.workingFolder, "SPECS")
	err = os.MkdirAll(specFolder, 0755)
	if err != nil {
		return "", err
	}
	pkgName := r.Spec.Header[PkgName]

	specFile, err := os.Create(filepath.Join(specFolder, pkgName+".spec"))
	if err != nil {
		return "", err
	}
	defer specFile.Close()
	err = r.Spec.Write(specFile)
	if err != nil {
		return "", err
	}
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command("rpmbuild", "-bb", specFile.Name())
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("rpmbuild failed with: %v. Stdout: %v. Stderr: %v", err, stdout.String(), stderr.String())
	}
	rpmFile := filepath.Join(rpmdir, r.Spec.Header[BuildArch], r.Spec.PackageName())
	_, err = os.Stat(rpmFile)
	if err != nil {
		return "", err
	}
	return rpmFile, nil
}

func (r *Rpm) movePackageFile(path string, info os.FileInfo, err error) error {
	if !info.IsDir() {
		if strings.HasSuffix(strings.ToLower(path), ".rpm") {
			return os.Rename(path, filepath.Base(path))
		}
	}
	return nil
}

func (r *Rpm) AddEmptyFolder(name string) error {
	destFolder := filepath.Join(r.buildRoot, name)
	err := os.MkdirAll(destFolder, 0755)
	if err != nil {
		return err
	}
	r.Spec.AddFile(name)
	return nil
}

func (r *Rpm) AddFolder(path string, prefix string) error {
	fc, err := files.New(path)
	if err != nil {
		return err
	}
	baseDir := filepath.Dir(path)
	for _, path := range fc.Files {
		targetPath := filepath.Join(prefix, strings.TrimPrefix(path, baseDir))
		err = r.AddFile(path, targetPath)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *Rpm) AddFile(sourcePath string, targetPath string) error {
	sourceInfo, err := os.Stat(sourcePath)
	if err != nil {
		return err
	}
	srcFile, err := os.Open(sourcePath)
	if err != nil {
		return err
	}
	defer srcFile.Close()
	destPath := filepath.Join(r.buildRoot, targetPath)
	err = os.MkdirAll(filepath.Dir(destPath), 0755)
	if err != nil {
		return err
	}

	destFile, err := os.OpenFile(destPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, sourceInfo.Mode())
	if err != nil {
		return err
	}
	defer destFile.Close()
	_, err = io.Copy(destFile, srcFile)
	if err != nil {
		return err
	}
	r.Spec.AddFile(targetPath)
	return nil
}
<|endoftext|>