<commit_before>package h2quic

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/qerr"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

type streamCreator interface {
	quic.Session
	GetOrOpenStream(protocol.StreamID) (quic.Stream, error)
}

type remoteCloser interface {
	CloseRemote(protocol.ByteCount)
}

// allows mocking of quic.Listen and quic.ListenAddr
var (
	quicListen     = quic.Listen
	quicListenAddr = quic.ListenAddr
)

// Server is a HTTP2 server listening for QUIC connections.
type Server struct {
	*http.Server

	// By providing a quic.Config, it is possible to set parameters of the QUIC connection.
	// If nil, it uses reasonable default values.
	QuicConfig *quic.Config

	// Private flag for demo, do not use
	CloseAfterFirstRequest bool

	port uint32 // used atomically

	listenerMutex sync.Mutex
	listener      quic.Listener
	closed        bool

	supportedVersionsAsString string

	logger utils.Logger // will be set by Server.serveImpl()
}

// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServe() error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	return s.serveImpl(s.TLSConfig, nil)
}

// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}
	return s.serveImpl(config, nil)
}

// Serve an existing UDP connection.
func (s *Server) Serve(conn net.PacketConn) error {
	return s.serveImpl(s.TLSConfig, conn)
}

func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	s.logger = utils.DefaultLogger.WithPrefix("server")
	s.listenerMutex.Lock()
	if s.closed {
		s.listenerMutex.Unlock()
		return errors.New("Server is already closed")
	}
	if s.listener != nil {
		s.listenerMutex.Unlock()
		return errors.New("ListenAndServe may only be called once")
	}

	var ln quic.Listener
	var err error
	if conn == nil {
		ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig)
	} else {
		ln, err = quicListen(conn, tlsConfig, s.QuicConfig)
	}
	if err != nil {
		s.listenerMutex.Unlock()
		return err
	}
	s.listener = ln
	s.listenerMutex.Unlock()

	for {
		sess, err := ln.Accept()
		if err != nil {
			return err
		}
		go s.handleHeaderStream(sess.(streamCreator))
	}
}

func (s *Server) handleHeaderStream(session streamCreator) {
	stream, err := session.AcceptStream()
	if err != nil {
		session.CloseWithError(quic.ErrorCode(qerr.InvalidHeadersStreamData), err)
		return
	}

	hpackDecoder := hpack.NewDecoder(4096, nil)
	h2framer := http2.NewFramer(nil, stream)

	var headerStreamMutex sync.Mutex // Protects concurrent calls to Write()
	for {
		if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil {
			// QuicErrors must originate from stream.Read() returning an error.
			// In this case, the session has already logged the error, so we don't
			// need to log it again.
			errorCode := qerr.InternalError
			if qerr, ok := err.(*qerr.QuicError); !ok {
				errorCode = qerr.ErrorCode
				s.logger.Errorf("error handling h2 request: %s", err.Error())
			}
			session.CloseWithError(quic.ErrorCode(errorCode), err)
			return
		}
	}
}

func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
	h2frame, err := h2framer.ReadFrame()
	if err != nil {
		return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
	}
	var h2headersFrame *http2.HeadersFrame
	switch f := h2frame.(type) {
	case *http2.PriorityFrame:
		// ignore PRIORITY frames
		s.logger.Debugf("Ignoring H2 PRIORITY frame: %#v", f)
		return nil
	case *http2.HeadersFrame:
		h2headersFrame = f
	default:
		return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
	}

	if !h2headersFrame.HeadersEnded() {
		return errors.New("http2 header continuation not implemented")
	}
	headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
	if err != nil {
		s.logger.Errorf("invalid http2 headers encoding: %s", err.Error())
		return err
	}

	req, err := requestFromHeaders(headers)
	if err != nil {
		return err
	}

	if s.logger.Debug() {
		s.logger.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID)
	} else {
		s.logger.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
	}

	dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
	if err != nil {
		return err
	}
	// this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request
	if dataStream == nil {
		return nil
	}

	// handleRequest should be as non-blocking as possible to minimize
	// head-of-line blocking. Potentially blocking code is run in a separate
	// goroutine, enabling handleRequest to return before the code is executed.
	go func() {
		streamEnded := h2headersFrame.StreamEnded()
		if streamEnded {
			dataStream.(remoteCloser).CloseRemote(0)
			streamEnded = true
			_, _ = dataStream.Read([]byte{0}) // read the eof
		}

		req = req.WithContext(dataStream.Context())
		reqBody := newRequestBody(dataStream)
		req.Body = reqBody

		req.RemoteAddr = session.RemoteAddr().String()

		responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID), s.logger)

		handler := s.Handler
		if handler == nil {
			handler = http.DefaultServeMux
		}
		panicked := false
		func() {
			defer func() {
				if p := recover(); p != nil {
					// Copied from net/http/server.go
					const size = 64 << 10
					buf := make([]byte, size)
					buf = buf[:runtime.Stack(buf, false)]
					s.logger.Errorf("http: panic serving: %v\n%s", p, buf)
					panicked = true
				}
			}()
			handler.ServeHTTP(responseWriter, req)
		}()
		if panicked {
			responseWriter.WriteHeader(500)
		} else {
			responseWriter.WriteHeader(200)
		}
		if responseWriter.dataStream != nil {
			if !streamEnded && !reqBody.requestRead {
				// in gQUIC, the error code doesn't matter, so just use 0 here
				responseWriter.dataStream.CancelRead(0)
			}
			responseWriter.dataStream.Close()
		}
		if s.CloseAfterFirstRequest {
			time.Sleep(100 * time.Millisecond)
			session.Close()
		}
	}()

	return nil
}

// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) Close() error {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	s.closed = true
	if s.listener != nil {
		err := s.listener.Close()
		s.listener = nil
		return err
	}
	return nil
}

// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.
// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) CloseGracefully(timeout time.Duration) error {
	// TODO: implement
	return nil
}

// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
//  Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
func (s *Server) SetQuicHeaders(hdr http.Header) error {
	port := atomic.LoadUint32(&s.port)

	if port == 0 {
		// Extract port from s.Server.Addr
		_, portStr, err := net.SplitHostPort(s.Server.Addr)
		if err != nil {
			return err
		}
		portInt, err := net.LookupPort("tcp", portStr)
		if err != nil {
			return err
		}
		port = uint32(portInt)
		atomic.StoreUint32(&s.port, port)
	}

	if s.supportedVersionsAsString == "" {
		var versions []string
		for _, v := range protocol.SupportedVersions {
			versions = append(versions, v.ToAltSvc())
		}
		s.supportedVersionsAsString = strings.Join(versions, ",")
	}

	hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))

	return nil
}

// ListenAndServeQUIC listens on the UDP network address addr and calls the
// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is
// used when handler is nil.
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
	server := &Server{
		Server: &http.Server{
			Addr:    addr,
			Handler: handler,
		},
	}
	return server.ListenAndServeTLS(certFile, keyFile)
}

// ListenAndServe listens on the given network address for both, TLS and QUIC
// connetions in parallel. It returns if one of the two returns an error.
// http.DefaultServeMux is used when handler is nil.
// The correct Alt-Svc headers for QUIC are set.
func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error {
	// Load certs
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}

	// Open the listeners
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}
	udpConn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return err
	}
	defer udpConn.Close()

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	tcpConn, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	defer tcpConn.Close()

	tlsConn := tls.NewListener(tcpConn, config)
	defer tlsConn.Close()

	// Start the servers
	httpServer := &http.Server{
		Addr:      addr,
		TLSConfig: config,
	}

	quicServer := &Server{
		Server: httpServer,
	}

	if handler == nil {
		handler = http.DefaultServeMux
	}
	httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		quicServer.SetQuicHeaders(w.Header())
		handler.ServeHTTP(w, r)
	})

	hErr := make(chan error)
	qErr := make(chan error)
	go func() {
		hErr <- httpServer.Serve(tlsConn)
	}()
	go func() {
		qErr <- quicServer.Serve(udpConn)
	}()

	select {
	case err := <-hErr:
		quicServer.Close()
		return err
	case err := <-qErr:
		// Cannot close the HTTP server or wait for requests to complete properly :/
		return err
	}
}
<commit_msg>fix incorrect type assertion in h2quic server<commit_after>package h2quic

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	quic "github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/internal/protocol"
	"github.com/lucas-clemente/quic-go/internal/utils"
	"github.com/lucas-clemente/quic-go/qerr"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"
)

type streamCreator interface {
	quic.Session
	GetOrOpenStream(protocol.StreamID) (quic.Stream, error)
}

type remoteCloser interface {
	CloseRemote(protocol.ByteCount)
}

// allows mocking of quic.Listen and quic.ListenAddr
var (
	quicListen     = quic.Listen
	quicListenAddr = quic.ListenAddr
)

// Server is a HTTP2 server listening for QUIC connections.
type Server struct {
	*http.Server

	// By providing a quic.Config, it is possible to set parameters of the QUIC connection.
	// If nil, it uses reasonable default values.
	QuicConfig *quic.Config

	// Private flag for demo, do not use
	CloseAfterFirstRequest bool

	port uint32 // used atomically

	listenerMutex sync.Mutex
	listener      quic.Listener
	closed        bool

	supportedVersionsAsString string

	logger utils.Logger // will be set by Server.serveImpl()
}

// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServe() error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	return s.serveImpl(s.TLSConfig, nil)
}

// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}
	return s.serveImpl(config, nil)
}

// Serve an existing UDP connection.
func (s *Server) Serve(conn net.PacketConn) error {
	return s.serveImpl(s.TLSConfig, conn)
}

func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
	if s.Server == nil {
		return errors.New("use of h2quic.Server without http.Server")
	}
	s.logger = utils.DefaultLogger.WithPrefix("server")
	s.listenerMutex.Lock()
	if s.closed {
		s.listenerMutex.Unlock()
		return errors.New("Server is already closed")
	}
	if s.listener != nil {
		s.listenerMutex.Unlock()
		return errors.New("ListenAndServe may only be called once")
	}

	var ln quic.Listener
	var err error
	if conn == nil {
		ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig)
	} else {
		ln, err = quicListen(conn, tlsConfig, s.QuicConfig)
	}
	if err != nil {
		s.listenerMutex.Unlock()
		return err
	}
	s.listener = ln
	s.listenerMutex.Unlock()

	for {
		sess, err := ln.Accept()
		if err != nil {
			return err
		}
		go s.handleHeaderStream(sess.(streamCreator))
	}
}

func (s *Server) handleHeaderStream(session streamCreator) {
	stream, err := session.AcceptStream()
	if err != nil {
		session.CloseWithError(quic.ErrorCode(qerr.InvalidHeadersStreamData), err)
		return
	}

	hpackDecoder := hpack.NewDecoder(4096, nil)
	h2framer := http2.NewFramer(nil, stream)

	var headerStreamMutex sync.Mutex // Protects concurrent calls to Write()
	for {
		if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil {
			// QuicErrors must originate from stream.Read() returning an error.
			// In this case, the session has already logged the error, so we don't
			// need to log it again.
			errorCode := qerr.InternalError
			if qerr, ok := err.(*qerr.QuicError); ok {
				errorCode = qerr.ErrorCode
				s.logger.Errorf("error handling h2 request: %s", err.Error())
			}
			session.CloseWithError(quic.ErrorCode(errorCode), err)
			return
		}
	}
}

func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
	h2frame, err := h2framer.ReadFrame()
	if err != nil {
		return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
	}
	var h2headersFrame *http2.HeadersFrame
	switch f := h2frame.(type) {
	case *http2.PriorityFrame:
		// ignore PRIORITY frames
		s.logger.Debugf("Ignoring H2 PRIORITY frame: %#v", f)
		return nil
	case *http2.HeadersFrame:
		h2headersFrame = f
	default:
		return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
	}

	if !h2headersFrame.HeadersEnded() {
		return errors.New("http2 header continuation not implemented")
	}
	headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment())
	if err != nil {
		s.logger.Errorf("invalid http2 headers encoding: %s", err.Error())
		return err
	}

	req, err := requestFromHeaders(headers)
	if err != nil {
		return err
	}

	if s.logger.Debug() {
		s.logger.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID)
	} else {
		s.logger.Infof("%s %s%s", req.Method, req.Host, req.RequestURI)
	}

	dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID))
	if err != nil {
		return err
	}
	// this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request
	if dataStream == nil {
		return nil
	}

	// handleRequest should be as non-blocking as possible to minimize
	// head-of-line blocking. Potentially blocking code is run in a separate
	// goroutine, enabling handleRequest to return before the code is executed.
	go func() {
		streamEnded := h2headersFrame.StreamEnded()
		if streamEnded {
			dataStream.(remoteCloser).CloseRemote(0)
			streamEnded = true
			_, _ = dataStream.Read([]byte{0}) // read the eof
		}

		req = req.WithContext(dataStream.Context())
		reqBody := newRequestBody(dataStream)
		req.Body = reqBody

		req.RemoteAddr = session.RemoteAddr().String()

		responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID), s.logger)

		handler := s.Handler
		if handler == nil {
			handler = http.DefaultServeMux
		}
		panicked := false
		func() {
			defer func() {
				if p := recover(); p != nil {
					// Copied from net/http/server.go
					const size = 64 << 10
					buf := make([]byte, size)
					buf = buf[:runtime.Stack(buf, false)]
					s.logger.Errorf("http: panic serving: %v\n%s", p, buf)
					panicked = true
				}
			}()
			handler.ServeHTTP(responseWriter, req)
		}()
		if panicked {
			responseWriter.WriteHeader(500)
		} else {
			responseWriter.WriteHeader(200)
		}
		if responseWriter.dataStream != nil {
			if !streamEnded && !reqBody.requestRead {
				// in gQUIC, the error code doesn't matter, so just use 0 here
				responseWriter.dataStream.CancelRead(0)
			}
			responseWriter.dataStream.Close()
		}
		if s.CloseAfterFirstRequest {
			time.Sleep(100 * time.Millisecond)
			session.Close()
		}
	}()

	return nil
}

// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) Close() error {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	s.closed = true
	if s.listener != nil {
		err := s.listener.Close()
		s.listener = nil
		return err
	}
	return nil
}

// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.
// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
func (s *Server) CloseGracefully(timeout time.Duration) error {
	// TODO: implement
	return nil
}

// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC.
// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443):
//  Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30"
func (s *Server) SetQuicHeaders(hdr http.Header) error {
	port := atomic.LoadUint32(&s.port)

	if port == 0 {
		// Extract port from s.Server.Addr
		_, portStr, err := net.SplitHostPort(s.Server.Addr)
		if err != nil {
			return err
		}
		portInt, err := net.LookupPort("tcp", portStr)
		if err != nil {
			return err
		}
		port = uint32(portInt)
		atomic.StoreUint32(&s.port, port)
	}

	if s.supportedVersionsAsString == "" {
		var versions []string
		for _, v := range protocol.SupportedVersions {
			versions = append(versions, v.ToAltSvc())
		}
		s.supportedVersionsAsString = strings.Join(versions, ",")
	}

	hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString))

	return nil
}

// ListenAndServeQUIC listens on the UDP network address addr and calls the
// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is
// used when handler is nil.
func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error {
	server := &Server{
		Server: &http.Server{
			Addr:    addr,
			Handler: handler,
		},
	}
	return server.ListenAndServeTLS(certFile, keyFile)
}

// ListenAndServe listens on the given network address for both, TLS and QUIC
// connetions in parallel. It returns if one of the two returns an error.
// http.DefaultServeMux is used when handler is nil.
// The correct Alt-Svc headers for QUIC are set.
func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error {
	// Load certs
	var err error
	certs := make([]tls.Certificate, 1)
	certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	// We currently only use the cert-related stuff from tls.Config,
	// so we don't need to make a full copy.
	config := &tls.Config{
		Certificates: certs,
	}

	// Open the listeners
	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}
	udpConn, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
		return err
	}
	defer udpConn.Close()

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	tcpConn, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	defer tcpConn.Close()

	tlsConn := tls.NewListener(tcpConn, config)
	defer tlsConn.Close()

	// Start the servers
	httpServer := &http.Server{
		Addr:      addr,
		TLSConfig: config,
	}

	quicServer := &Server{
		Server: httpServer,
	}

	if handler == nil {
		handler = http.DefaultServeMux
	}
	httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		quicServer.SetQuicHeaders(w.Header())
		handler.ServeHTTP(w, r)
	})

	hErr := make(chan error)
	qErr := make(chan error)
	go func() {
		hErr <- httpServer.Serve(tlsConn)
	}()
	go func() {
		qErr <- quicServer.Serve(udpConn)
	}()

	select {
	case err := <-hErr:
		quicServer.Close()
		return err
	case err := <-qErr:
		// Cannot close the HTTP server or wait for requests to complete properly :/
		return err
	}
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "io/ioutil"
    "os"
    "strings"
    "fmt"
)

type Expr struct {
    Left *Expr
    Operator string
    Right *Expr
    Value string
}

func inspect(expr *Expr) string {
    if len(expr.Value) > 0 {
        return expr.Value
    }
    return fmt.Sprintf("(%s %s %s)", inspect(expr.Left), expr.Operator, inspect(expr.Right))
}

func getFieldValue(field string) string {
    switch field {
    case "amount": return "1000"
    case "description": return "Fuel from caltex"
    case "price": return "200"
    case "comments": return "switched from myob"
    case "name": return "xero accounting"
    default: panic(fmt.Sprintf("Field %s is not implemented", field))
    }
}

func execute(expr *Expr) bool {
    if len(expr.Value) > 0 {
        return true
    }
    switch expr.Operator {
    case "and":
        return execute(expr.Left) && execute(expr.Right)
    case "or":
        return execute(expr.Left) || execute(expr.Right)
    case "equals":
        return getFieldValue(expr.Left.Value) == expr.Right.Value
    case "contains":
        return strings.Contains(getFieldValue(expr.Left.Value), expr.Right.Value)
    default:
        panic(fmt.Sprintf("Operator %s is not implemented", expr.Operator))
    }
}

func main() {
    if jsonBlob, err := ioutil.ReadAll(os.Stdin); err != nil {
        fmt.Println("read error:", err)
    } else {
        var expr Expr
        err := json.Unmarshal(jsonBlob, &expr)
        if err != nil {
            fmt.Println("json error:", err)
        } else {
            fmt.Printf("%s = %t\n", inspect(&expr), execute(&expr))
        }
    }
}<commit_msg>go fmt<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

type Expr struct {
	Left     *Expr
	Operator string
	Right    *Expr
	Value    string
}

func inspect(expr *Expr) string {
	if len(expr.Value) > 0 {
		return expr.Value
	}
	return fmt.Sprintf("(%s %s %s)", inspect(expr.Left), expr.Operator, inspect(expr.Right))
}

func getFieldValue(field string) string {
	switch field {
	case "amount":
		return "1000"
	case "description":
		return "Fuel from caltex"
	case "price":
		return "200"
	case "comments":
		return "switched from myob"
	case "name":
		return "xero accounting"
	default:
		panic(fmt.Sprintf("Field %s is not implemented", field))
	}
}

func execute(expr *Expr) bool {
	if len(expr.Value) > 0 {
		return true
	}
	switch expr.Operator {
	case "and":
		return execute(expr.Left) && execute(expr.Right)
	case "or":
		return execute(expr.Left) || execute(expr.Right)
	case "equals":
		return getFieldValue(expr.Left.Value) == expr.Right.Value
	case "contains":
		return strings.Contains(getFieldValue(expr.Left.Value), expr.Right.Value)
	default:
		panic(fmt.Sprintf("Operator %s is not implemented", expr.Operator))
	}
}

func main() {
	if jsonBlob, err := ioutil.ReadAll(os.Stdin); err != nil {
		fmt.Println("read error:", err)
	} else {
		var expr Expr
		err := json.Unmarshal(jsonBlob, &expr)
		if err != nil {
			fmt.Println("json error:", err)
		} else {
			fmt.Printf("%s = %t\n", inspect(&expr), execute(&expr))
		}
	}
}
<|endoftext|>


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

package zk

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"path"
	"sort"
	"strings"
	"sync"
	"time"

	"launchpad.net/gozk/zookeeper"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// Create a path and any pieces required, think mkdir -p.
// Intermediate znodes are always created empty.
func CreateRecursive(zconn Conn, zkPath, value string, flags int, aclv []zookeeper.ACL) (pathCreated string, err error) {
	parts := strings.Split(zkPath, "/")
	if parts[1] != "zk" {
		return "", fmt.Errorf("zkutil: non zk path: %v", zkPath)
	}

	pathCreated, err = zconn.Create(zkPath, value, flags, aclv)
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
		_, err = CreateRecursive(zconn, path.Dir(zkPath), "", flags, aclv)
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			return "", err
		}
		pathCreated, err = zconn.Create(zkPath, value, flags, aclv)
	}
	return
}

func CreateOrUpdate(zconn Conn, zkPath, value string, flags int, aclv []zookeeper.ACL, recursive bool) (pathCreated string, err error) {
	if recursive {
		pathCreated, err = CreateRecursive(zconn, zkPath, value, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
	} else {
		pathCreated, err = zconn.Create(zkPath, value, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
	}
	if err != nil && zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
		pathCreated = ""
		_, err = zconn.Set(zkPath, value, -1)
	}
	return
}

type pathItem struct {
	path string
	err  error
}

func ChildrenRecursive(zconn Conn, zkPath string) ([]string, error) {
	var err error
	mutex := sync.Mutex{}
	wg := sync.WaitGroup{}
	pathList := make([]string, 0, 32)
	children, _, err := zconn.Children(zkPath)
	if err != nil {
		return nil, err
	}

	for _, child := range children {
		wg.Add(1)
		go func(child string) {
			childPath := path.Join(zkPath, child)
			rChildren, zkErr := ChildrenRecursive(zconn, childPath)
			if zkErr != nil {
				// If other processes are deleting nodes, we need to ignore
				// the missing nodes.
				if !zookeeper.IsError(zkErr, zookeeper.ZNONODE) {
					mutex.Lock()
					err = zkErr
					mutex.Unlock()
				}
			} else {
				mutex.Lock()
				pathList = append(pathList, child)
				for _, rChild := range rChildren {
					pathList = append(pathList, path.Join(child, rChild))
				}
				mutex.Unlock()
			}
			wg.Done()
		}(child)
	}

	wg.Wait()

	mutex.Lock()
	defer mutex.Unlock()
	if err != nil {
		return nil, err
	}
	return pathList, nil
}

// resolve paths like:
// /zk/nyc/vt/tablets/*/action
// /zk/global/vt/keyspaces/*/shards/*/action
// /zk/*/vt/tablets/*/action
// into real existing paths
//
// If you send paths that don't contain any wildcard and
// don't exist, this function will return an empty array.
func ResolveWildcards(zconn Conn, zkPaths []string) ([]string, error) {
	// check all the paths start with /zk/ before doing anything
	// time consuming
	for _, zkPath := range zkPaths {

		if _, err := ZkCellFromZkPath(zkPath); err != nil {
			return nil, err
		}
	}

	results := make([][]string, len(zkPaths))
	wg := &sync.WaitGroup{}
	mu := &sync.Mutex{}
	var firstError error

	for i, zkPath := range zkPaths {
		wg.Add(1)
		parts := strings.Split(zkPath, "/")
		go func(i int) {
			defer wg.Done()
			subResult, err := resolveRecursive(zconn, parts, true)
			if err != nil {
				mu.Lock()
				if firstError != nil {
					log.Printf("Multiple error: %v", err)
				} else {
					firstError = err
				}
				mu.Unlock()
			} else {
				results[i] = subResult
			}
		}(i)
	}

	wg.Wait()
	if firstError != nil {
		return nil, firstError
	}

	result := make([]string, 0, 32)
	for i := 0; i < len(zkPaths); i++ {
		subResult := results[i]
		if subResult != nil {
			result = append(result, subResult...)
		}
	}

	return result, nil
}

// checks if a string has a wildcard in it. In the cases we detect a bad
// pattern, we return 'true', and let the path.Match function find it.
func hasWildcard(path string) bool {
	for i := 0; i < len(path); i++ {
		switch path[i] {
		case '\\':
			if i+1 >= len(path) {
				return true
			} else {
				i++
			}
		case '*', '?', '[':
			return true
		}
	}
	return false
}

func resolveRecursive(zconn Conn, parts []string, toplevel bool) ([]string, error) {
	for i, part := range parts {
		if hasWildcard(part) {
			var children []string
			if i == 2 {
				children = ZkKnownCells(false)
			} else {
				zkParentPath := strings.Join(parts[:i], "/")
				var err error
				children, _, err = zconn.Children(zkParentPath)
				if err != nil {
					// we asked for something like
					// /zk/cell/aaa/* and
					// /zk/cell/aaa doesn't exist
					// -> return empty list, no error
					// (note we check both a regular zk
					// error and the error the test
					// produces)
					if zookeeper.IsError(err, zookeeper.ZNONODE) {
						return nil, nil
					}
					// otherwise we return the error
					return nil, err
				}
			}
			sort.Strings(children)

			results := make([][]string, len(children))
			wg := &sync.WaitGroup{}
			mu := &sync.Mutex{}
			var firstError error

			for j, child := range children {
				matched, err := path.Match(part, child)
				if err != nil {
					return nil, err
				}
				if matched {
					// we have a match!
					wg.Add(1)
					newParts := make([]string, len(parts))
					copy(newParts, parts)
					newParts[i] = child
					go func(j int) {
						defer wg.Done()
						subResult, err := resolveRecursive(zconn, newParts, false)
						if err != nil {
							mu.Lock()
							if firstError != nil {
								log.Printf("Multiple error: %v", err)
							} else {
								firstError = err
							}
							mu.Unlock()
						} else {
							results[j] = subResult
						}
					}(j)
				}
			}

			wg.Wait()
			if firstError != nil {
				return nil, firstError
			}

			result := make([]string, 0, 32)
			for j := 0; j < len(children); j++ {
				subResult := results[j]
				if subResult != nil {
					result = append(result, subResult...)
				}
			}

			// we found a part that is a wildcard, we
			// added the children already, we're done
			return result, nil
		}
	}

	// no part contains a wildcard, add the path if it exists, and done
	path := strings.Join(parts, "/")
	if toplevel {
		// for whatever the user typed at the toplevel, we don't
		// check it exists or not, we just return it
		return []string{path}, nil
	}

	// this is an expanded path, we need to check if it exists
	stat, err := zconn.Exists(path)
	if err != nil {
		return nil, err
	}
	if stat != nil {
		return []string{path}, nil
	}
	return nil, nil
}

func DeleteRecursive(zconn Conn, zkPath string, version int) error {
	// version: -1 delete any version of the node at path - only applies to the top node
	err := zconn.Delete(zkPath, version)
	if err == nil {
		return nil
	}
	if !zookeeper.IsError(err, zookeeper.ZNOTEMPTY) {
		return err
	}
	// Remove the ability for other nodes to get created while we are trying to delete.
	// Otherwise, you can enter a race condition, or get starved out from deleting.
	err = zconn.SetACL(zkPath, zookeeper.WorldACL(zookeeper.PERM_ADMIN|zookeeper.PERM_DELETE|zookeeper.PERM_READ), version)
	if err != nil {
		return err
	}
	children, _, err := zconn.Children(zkPath)
	if err != nil {
		return err
	}
	for _, child := range children {
		err := DeleteRecursive(zconn, path.Join(zkPath, child), -1)
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNONODE) {
			return fmt.Errorf("zkutil: recursive delete failed: %v", err)
		}
	}

	err = zconn.Delete(zkPath, version)
	if err != nil && !zookeeper.IsError(err, zookeeper.ZNOTEMPTY) {
		err = fmt.Errorf("zkutil: nodes getting recreated underneath delete (app race condition): %v", zkPath)
	}
	return err
}

// The lexically lowest node is the lock holder - verify that this
// path holds the lock.  Call this queue-lock because the semantics are
// a hybrid.  Normal zookeeper locks make assumptions about sequential
// numbering that don't hold when the data in a lock is modified.
// if the provided 'interrupted' chan is closed, we'll just stop waiting
// and return an interruption error
func ObtainQueueLock(zconn Conn, zkPath string, wait time.Duration, interrupted chan struct{}) error {
	queueNode := path.Dir(zkPath)
	lockNode := path.Base(zkPath)

	timer := time.NewTimer(wait)
trylock:
	children, _, err := zconn.Children(queueNode)
	if err != nil {
		return fmt.Errorf("zkutil: trylock failed %v", err)
	}
	sort.Strings(children)
	if len(children) > 0 {
		if children[0] == lockNode {
			return nil
		}
		if wait > 0 {
			prevLock := ""
			for i := 1; i < len(children); i++ {
				if children[i] == lockNode {
					prevLock = children[i-1]
					break
				}
			}
			if prevLock == "" {
				return fmt.Errorf("zkutil: no previous queue node found: %v", zkPath)
			}

			zkPrevLock := path.Join(queueNode, prevLock)
			stat, watch, err := zconn.ExistsW(zkPrevLock)
			if err != nil {
				return fmt.Errorf("zkutil: unable to watch queued node %v %v", zkPrevLock, err)
			}
			if stat == nil {
				goto trylock
			}
			select {
			case <-timer.C:
				break
			case <-interrupted:
				return fmt.Errorf("zkutil: obtaining lock was interrupted %v", zkPath)
			case <-watch:
				// The precise event doesn't matter - try to read again regardless.
				goto trylock
			}
		}
		return fmt.Errorf("zkutil: obtaining lock timed out %v", zkPath)
	}
	return fmt.Errorf("zkutil: empty queue node: %v", queueNode)
}

// Close done when you want to exit cleanly.
func CreatePidNode(zconn Conn, zkPath string, done chan struct{}) error {
	hostname, err := os.Hostname()
	if err != nil {
		return fmt.Errorf("zkutil: failed creating pid node %v: %v", zkPath, err)
	}
	data := fmt.Sprintf("host:%v\npid:%v\n", hostname, os.Getpid())

	// On the first try, assume the cluster is up and running, that will
	// help hunt down any config issues present at startup
	_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
	if err != nil {
		if zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			err = zconn.Delete(zkPath, -1)
		}
		if err != nil {
			return fmt.Errorf("zkutil: failed deleting pid node: %v: %v", zkPath, err)
		}
		_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
		if err != nil {
			return fmt.Errorf("zkutil: failed creating pid node: %v: %v", zkPath, err)
		}
	}

	go func() {
		for {
			_, _, watch, err := zconn.GetW(zkPath)
			if err != nil {
				if zookeeper.IsError(err, zookeeper.ZNONODE) {
					_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
					if err == nil {
						log.Printf("WARNING: failed recreating pid node: %v: %v", zkPath, err)
					} else {
						log.Printf("INFO: recreated pid node: %v", zkPath)
						continue
					}
				} else {
					log.Printf("WARNING: failed reading pid node: %v", err)
				}
			} else {
				select {
				case event := <-watch:
					if event.Ok() && event.Type == zookeeper.EVENT_DELETED {
						// Most likely another process has started up. However,
						// there is a chance that an ephemeral node is deleted by
						// the session expiring, yet that same session gets a watch
						// notification. This seems like buggy behavior, but rather
						// than race too hard on the node, just wait a bit and see
						// if the situation resolves itself.
						log.Printf("WARNING: pid deleted: %v", zkPath)
					} else {
						log.Printf("INFO: pid node event: %v", event)
					}
					// break here and wait for a bit before attempting
				case <-done:
					log.Printf("INFO: pid watcher stopped on done: %v", zkPath)
					return
				}
			}
			select {
			// No one likes a thundering herd, least of all zookeeper.
			case <-time.After(5*time.Second + time.Duration(rand.Int63n(55e9))):
			case <-done:
				log.Printf("INFO: pid watcher stopped on done: %v", zkPath)
				return
			}
		}
	}()

	return nil
}
<commit_msg>relax ResolveWildcards check so that this function may be used without a metaconn<commit_after>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package zk

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"path"
	"sort"
	"strings"
	"sync"
	"time"

	"launchpad.net/gozk/zookeeper"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// Create a path and any pieces required, think mkdir -p.
// Intermediate znodes are always created empty.
func CreateRecursive(zconn Conn, zkPath, value string, flags int, aclv []zookeeper.ACL) (pathCreated string, err error) {
	parts := strings.Split(zkPath, "/")
	if parts[1] != "zk" {
		return "", fmt.Errorf("zkutil: non zk path: %v", zkPath)
	}

	pathCreated, err = zconn.Create(zkPath, value, flags, aclv)
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
		_, err = CreateRecursive(zconn, path.Dir(zkPath), "", flags, aclv)
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			return "", err
		}
		pathCreated, err = zconn.Create(zkPath, value, flags, aclv)
	}
	return
}

func CreateOrUpdate(zconn Conn, zkPath, value string, flags int, aclv []zookeeper.ACL, recursive bool) (pathCreated string, err error) {
	if recursive {
		pathCreated, err = CreateRecursive(zconn, zkPath, value, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
	} else {
		pathCreated, err = zconn.Create(zkPath, value, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
	}
	if err != nil && zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
		pathCreated = ""
		_, err = zconn.Set(zkPath, value, -1)
	}
	return
}

type pathItem struct {
	path string
	err  error
}

func ChildrenRecursive(zconn Conn, zkPath string) ([]string, error) {
	var err error
	mutex := sync.Mutex{}
	wg := sync.WaitGroup{}
	pathList := make([]string, 0, 32)
	children, _, err := zconn.Children(zkPath)
	if err != nil {
		return nil, err
	}

	for _, child := range children {
		wg.Add(1)
		go func(child string) {
			childPath := path.Join(zkPath, child)
			rChildren, zkErr := ChildrenRecursive(zconn, childPath)
			if zkErr != nil {
				// If other processes are deleting nodes, we need to ignore
				// the missing nodes.
				if !zookeeper.IsError(zkErr, zookeeper.ZNONODE) {
					mutex.Lock()
					err = zkErr
					mutex.Unlock()
				}
			} else {
				mutex.Lock()
				pathList = append(pathList, child)
				for _, rChild := range rChildren {
					pathList = append(pathList, path.Join(child, rChild))
				}
				mutex.Unlock()
			}
			wg.Done()
		}(child)
	}

	wg.Wait()

	mutex.Lock()
	defer mutex.Unlock()
	if err != nil {
		return nil, err
	}
	return pathList, nil
}

// resolve paths like:
// /zk/nyc/vt/tablets/*/action
// /zk/global/vt/keyspaces/*/shards/*/action
// /zk/*/vt/tablets/*/action
// into real existing paths
//
// If you send paths that don't contain any wildcard and
// don't exist, this function will return an empty array.
func ResolveWildcards(zconn Conn, zkPaths []string) ([]string, error) {
	// check all the paths start with /zk/ before doing anything
	// time consuming
	// relax this in case we are not talking to a metaconn and
	// just want to talk to a specified instance.
	// for _, zkPath := range zkPaths {
	// 	if _, err := ZkCellFromZkPath(zkPath); err != nil {
	// 		return nil, err
	// 	}
	// }

	results := make([][]string, len(zkPaths))
	wg := &sync.WaitGroup{}
	mu := &sync.Mutex{}
	var firstError error

	for i, zkPath := range zkPaths {
		wg.Add(1)
		parts := strings.Split(zkPath, "/")
		go func(i int) {
			defer wg.Done()
			subResult, err := resolveRecursive(zconn, parts, true)
			if err != nil {
				mu.Lock()
				if firstError != nil {
					log.Printf("Multiple error: %v", err)
				} else {
					firstError = err
				}
				mu.Unlock()
			} else {
				results[i] = subResult
			}
		}(i)
	}

	wg.Wait()
	if firstError != nil {
		return nil, firstError
	}

	result := make([]string, 0, 32)
	for i := 0; i < len(zkPaths); i++ {
		subResult := results[i]
		if subResult != nil {
			result = append(result, subResult...)
		}
	}

	return result, nil
}

// checks if a string has a wildcard in it. In the cases we detect a bad
// pattern, we return 'true', and let the path.Match function find it.
func hasWildcard(path string) bool {
	for i := 0; i < len(path); i++ {
		switch path[i] {
		case '\\':
			if i+1 >= len(path) {
				return true
			} else {
				i++
			}
		case '*', '?', '[':
			return true
		}
	}
	return false
}

func resolveRecursive(zconn Conn, parts []string, toplevel bool) ([]string, error) {
	for i, part := range parts {
		if hasWildcard(part) {
			var children []string
			if i == 2 {
				children = ZkKnownCells(false)
			} else {
				zkParentPath := strings.Join(parts[:i], "/")
				var err error
				children, _, err = zconn.Children(zkParentPath)
				if err != nil {
					// we asked for something like
					// /zk/cell/aaa/* and
					// /zk/cell/aaa doesn't exist
					// -> return empty list, no error
					// (note we check both a regular zk
					// error and the error the test
					// produces)
					if zookeeper.IsError(err, zookeeper.ZNONODE) {
						return nil, nil
					}
					// otherwise we return the error
					return nil, err
				}
			}
			sort.Strings(children)

			results := make([][]string, len(children))
			wg := &sync.WaitGroup{}
			mu := &sync.Mutex{}
			var firstError error

			for j, child := range children {
				matched, err := path.Match(part, child)
				if err != nil {
					return nil, err
				}
				if matched {
					// we have a match!
					wg.Add(1)
					newParts := make([]string, len(parts))
					copy(newParts, parts)
					newParts[i] = child
					go func(j int) {
						defer wg.Done()
						subResult, err := resolveRecursive(zconn, newParts, false)
						if err != nil {
							mu.Lock()
							if firstError != nil {
								log.Printf("Multiple error: %v", err)
							} else {
								firstError = err
							}
							mu.Unlock()
						} else {
							results[j] = subResult
						}
					}(j)
				}
			}

			wg.Wait()
			if firstError != nil {
				return nil, firstError
			}

			result := make([]string, 0, 32)
			for j := 0; j < len(children); j++ {
				subResult := results[j]
				if subResult != nil {
					result = append(result, subResult...)
				}
			}

			// we found a part that is a wildcard, we
			// added the children already, we're done
			return result, nil
		}
	}

	// no part contains a wildcard, add the path if it exists, and done
	path := strings.Join(parts, "/")
	if toplevel {
		// for whatever the user typed at the toplevel, we don't
		// check it exists or not, we just return it
		return []string{path}, nil
	}

	// this is an expanded path, we need to check if it exists
	stat, err := zconn.Exists(path)
	if err != nil {
		return nil, err
	}
	if stat != nil {
		return []string{path}, nil
	}
	return nil, nil
}

func DeleteRecursive(zconn Conn, zkPath string, version int) error {
	// version: -1 delete any version of the node at path - only applies to the top node
	err := zconn.Delete(zkPath, version)
	if err == nil {
		return nil
	}
	if !zookeeper.IsError(err, zookeeper.ZNOTEMPTY) {
		return err
	}
	// Remove the ability for other nodes to get created while we are trying to delete.
	// Otherwise, you can enter a race condition, or get starved out from deleting.
	err = zconn.SetACL(zkPath, zookeeper.WorldACL(zookeeper.PERM_ADMIN|zookeeper.PERM_DELETE|zookeeper.PERM_READ), version)
	if err != nil {
		return err
	}
	children, _, err := zconn.Children(zkPath)
	if err != nil {
		return err
	}
	for _, child := range children {
		err := DeleteRecursive(zconn, path.Join(zkPath, child), -1)
		if err != nil && !zookeeper.IsError(err, zookeeper.ZNONODE) {
			return fmt.Errorf("zkutil: recursive delete failed: %v", err)
		}
	}

	err = zconn.Delete(zkPath, version)
	if err != nil && !zookeeper.IsError(err, zookeeper.ZNOTEMPTY) {
		err = fmt.Errorf("zkutil: nodes getting recreated underneath delete (app race condition): %v", zkPath)
	}
	return err
}

// The lexically lowest node is the lock holder - verify that this
// path holds the lock.  Call this queue-lock because the semantics are
// a hybrid.  Normal zookeeper locks make assumptions about sequential
// numbering that don't hold when the data in a lock is modified.
// if the provided 'interrupted' chan is closed, we'll just stop waiting
// and return an interruption error
func ObtainQueueLock(zconn Conn, zkPath string, wait time.Duration, interrupted chan struct{}) error {
	queueNode := path.Dir(zkPath)
	lockNode := path.Base(zkPath)

	timer := time.NewTimer(wait)
trylock:
	children, _, err := zconn.Children(queueNode)
	if err != nil {
		return fmt.Errorf("zkutil: trylock failed %v", err)
	}
	sort.Strings(children)
	if len(children) > 0 {
		if children[0] == lockNode {
			return nil
		}
		if wait > 0 {
			prevLock := ""
			for i := 1; i < len(children); i++ {
				if children[i] == lockNode {
					prevLock = children[i-1]
					break
				}
			}
			if prevLock == "" {
				return fmt.Errorf("zkutil: no previous queue node found: %v", zkPath)
			}

			zkPrevLock := path.Join(queueNode, prevLock)
			stat, watch, err := zconn.ExistsW(zkPrevLock)
			if err != nil {
				return fmt.Errorf("zkutil: unable to watch queued node %v %v", zkPrevLock, err)
			}
			if stat == nil {
				goto trylock
			}
			select {
			case <-timer.C:
				break
			case <-interrupted:
				return fmt.Errorf("zkutil: obtaining lock was interrupted %v", zkPath)
			case <-watch:
				// The precise event doesn't matter - try to read again regardless.
				goto trylock
			}
		}
		return fmt.Errorf("zkutil: obtaining lock timed out %v", zkPath)
	}
	return fmt.Errorf("zkutil: empty queue node: %v", queueNode)
}

// Close done when you want to exit cleanly.
func CreatePidNode(zconn Conn, zkPath string, done chan struct{}) error {
	hostname, err := os.Hostname()
	if err != nil {
		return fmt.Errorf("zkutil: failed creating pid node %v: %v", zkPath, err)
	}
	data := fmt.Sprintf("host:%v\npid:%v\n", hostname, os.Getpid())

	// On the first try, assume the cluster is up and running, that will
	// help hunt down any config issues present at startup
	_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
	if err != nil {
		if zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
			err = zconn.Delete(zkPath, -1)
		}
		if err != nil {
			return fmt.Errorf("zkutil: failed deleting pid node: %v: %v", zkPath, err)
		}
		_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
		if err != nil {
			return fmt.Errorf("zkutil: failed creating pid node: %v: %v", zkPath, err)
		}
	}

	go func() {
		for {
			_, _, watch, err := zconn.GetW(zkPath)
			if err != nil {
				if zookeeper.IsError(err, zookeeper.ZNONODE) {
					_, err = zconn.Create(zkPath, data, zookeeper.EPHEMERAL, zookeeper.WorldACL(zookeeper.PERM_ALL))
					if err == nil {
						log.Printf("WARNING: failed recreating pid node: %v: %v", zkPath, err)
					} else {
						log.Printf("INFO: recreated pid node: %v", zkPath)
						continue
					}
				} else {
					log.Printf("WARNING: failed reading pid node: %v", err)
				}
			} else {
				select {
				case event := <-watch:
					if event.Ok() && event.Type == zookeeper.EVENT_DELETED {
						// Most likely another process has started up. However,
						// there is a chance that an ephemeral node is deleted by
						// the session expiring, yet that same session gets a watch
						// notification. This seems like buggy behavior, but rather
						// than race too hard on the node, just wait a bit and see
						// if the situation resolves itself.
						log.Printf("WARNING: pid deleted: %v", zkPath)
					} else {
						log.Printf("INFO: pid node event: %v", event)
					}
					// break here and wait for a bit before attempting
				case <-done:
					log.Printf("INFO: pid watcher stopped on done: %v", zkPath)
					return
				}
			}
			select {
			// No one likes a thundering herd, least of all zookeeper.
			case <-time.After(5*time.Second + time.Duration(rand.Int63n(55e9))):
			case <-done:
				log.Printf("INFO: pid watcher stopped on done: %v", zkPath)
				return
			}
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"

	"github.com/spf13/cobra"
)

var (
	projectVersion string

	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print version",
		Long:  "Print inagoctl version",
		Run:   versionRun,
	}
)

func versionRun(cmd *cobra.Command, args []string) {
	fmt.Printf("Inagoctl %s\n", projectVersion)
}
<commit_msg>lowercase "inagoctl" in version output<commit_after>package cli

import (
	"fmt"

	"github.com/spf13/cobra"
)

var (
	projectVersion string

	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print version",
		Long:  "Print inagoctl version",
		Run:   versionRun,
	}
)

func versionRun(cmd *cobra.Command, args []string) {
	fmt.Printf("inagoctl %s\n", projectVersion)
}
<|endoftext|>


<commit_before>package responses

import (
	"github.com/emersion/go-imap"
)

// A SELECT response.
type Select struct {
	Mailbox *imap.MailboxStatus
}

func (r *Select) HandleFrom(hdlr imap.RespHandler) (err error) {
	if r.Mailbox == nil {
		r.Mailbox = &imap.MailboxStatus{}
	}
	mbox := r.Mailbox

	mbox.Items = make(map[string]interface{})
	for h := range hdlr {
		switch res := h.Resp.(type) {
		case *imap.Resp:
			fields, ok := h.AcceptNamedResp(imap.MailboxFlags)
			if !ok {
				break
			}

			flags, _ := fields[0].([]interface{})
			mbox.Flags, _ = imap.ParseStringList(flags)
			mbox.ItemsLocker.Lock()
			mbox.Items[imap.MailboxFlags] = nil
			mbox.ItemsLocker.Unlock()
		case *imap.StatusResp:
			if len(res.Arguments) < 1 {
				h.Accepts <- false
				break
			}

			accepted := true
			switch res.Code {
			case imap.MailboxUnseen:
				mbox.Unseen, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUnseen] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxPermanentFlags:
				flags, _ := res.Arguments[0].([]interface{})
				mbox.PermanentFlags, _ = imap.ParseStringList(flags)
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxPermanentFlags] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxUidNext:
				mbox.UidNext, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUidNext] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxUidValidity:
				mbox.UidValidity, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUidValidity] = nil
				mbox.ItemsLocker.Unlock()
			default:
				accepted = false
			}
			h.Accepts <- accepted
		}
	}

	return
}

func (r *Select) WriteTo(w *imap.Writer) (err error) {
	status := r.Mailbox

	for k := range r.Mailbox.Items {
		switch k {
		case imap.MailboxFlags:
			flags := make([]interface{}, len(status.Flags))
			for i, f := range status.Flags {
				flags[i] = f
			}
			res := imap.NewUntaggedResp([]interface{}{"FLAGS", flags})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxPermanentFlags:
			flags := make([]interface{}, len(status.PermanentFlags))
			for i, f := range status.PermanentFlags {
				flags[i] = f
			}
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodePermanentFlags,
				Arguments: []interface{}{flags},
				Info:      "Flags permitted.",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxMessages:
			res := imap.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxRecent:
			res := imap.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUnseen:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUnseen,
				Arguments: []interface{}{status.Unseen},
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUidNext:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUidNext,
				Arguments: []interface{}{status.UidNext},
				Info:      "Predicted next UID",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUidValidity:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUidValidity,
				Arguments: []interface{}{status.UidValidity},
				Info:      "UIDs valid",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		}
	}

	return
}
<commit_msg>Adds info to OK [UNSEEN] response<commit_after>package responses

import (
	"fmt"

	"github.com/emersion/go-imap"
)

// A SELECT response.
type Select struct {
	Mailbox *imap.MailboxStatus
}

func (r *Select) HandleFrom(hdlr imap.RespHandler) (err error) {
	if r.Mailbox == nil {
		r.Mailbox = &imap.MailboxStatus{}
	}
	mbox := r.Mailbox

	mbox.Items = make(map[string]interface{})
	for h := range hdlr {
		switch res := h.Resp.(type) {
		case *imap.Resp:
			fields, ok := h.AcceptNamedResp(imap.MailboxFlags)
			if !ok {
				break
			}

			flags, _ := fields[0].([]interface{})
			mbox.Flags, _ = imap.ParseStringList(flags)
			mbox.ItemsLocker.Lock()
			mbox.Items[imap.MailboxFlags] = nil
			mbox.ItemsLocker.Unlock()
		case *imap.StatusResp:
			if len(res.Arguments) < 1 {
				h.Accepts <- false
				break
			}

			accepted := true
			switch res.Code {
			case imap.MailboxUnseen:
				mbox.Unseen, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUnseen] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxPermanentFlags:
				flags, _ := res.Arguments[0].([]interface{})
				mbox.PermanentFlags, _ = imap.ParseStringList(flags)
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxPermanentFlags] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxUidNext:
				mbox.UidNext, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUidNext] = nil
				mbox.ItemsLocker.Unlock()
			case imap.MailboxUidValidity:
				mbox.UidValidity, _ = imap.ParseNumber(res.Arguments[0])
				mbox.ItemsLocker.Lock()
				mbox.Items[imap.MailboxUidValidity] = nil
				mbox.ItemsLocker.Unlock()
			default:
				accepted = false
			}
			h.Accepts <- accepted
		}
	}

	return
}

func (r *Select) WriteTo(w *imap.Writer) (err error) {
	status := r.Mailbox

	for k := range r.Mailbox.Items {
		switch k {
		case imap.MailboxFlags:
			flags := make([]interface{}, len(status.Flags))
			for i, f := range status.Flags {
				flags[i] = f
			}
			res := imap.NewUntaggedResp([]interface{}{"FLAGS", flags})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxPermanentFlags:
			flags := make([]interface{}, len(status.PermanentFlags))
			for i, f := range status.PermanentFlags {
				flags[i] = f
			}
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodePermanentFlags,
				Arguments: []interface{}{flags},
				Info:      "Flags permitted.",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxMessages:
			res := imap.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxRecent:
			res := imap.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
			if err = res.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUnseen:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUnseen,
				Arguments: []interface{}{status.Unseen},
				Info:      fmt.Sprintf("Message %d is first unseen", status.Unseen),
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUidNext:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUidNext,
				Arguments: []interface{}{status.UidNext},
				Info:      "Predicted next UID",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		case imap.MailboxUidValidity:
			statusRes := &imap.StatusResp{
				Type:      imap.StatusOk,
				Code:      imap.CodeUidValidity,
				Arguments: []interface{}{status.UidValidity},
				Info:      "UIDs valid",
			}
			if err = statusRes.WriteTo(w); err != nil {
				return
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/netlify/gotrue/models"
)

const (
	jwsSignatureHeaderName = "x-nf-sign"
)

type NetlifyMicroserviceClaims struct {
	SiteURL    string `json:"site_url"`
	Env        string `json:"env"`
	InstanceID string `json:"id"`
	NetlifyID  string `json:"netlify_id"`
	jwt.StandardClaims
}

func addGetBody(w http.ResponseWriter, req *http.Request) (context.Context, error) {
	if req.Method == http.MethodGet {
		return req.Context(), nil
	}

	if req.Body == nil || req.Body == http.NoBody {
		return nil, badRequestError("request must provide a body")
	}

	buf, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, internalServerError("Error reading body").WithInternalError(err)
	}
	req.GetBody = func() (io.ReadCloser, error) {
		return ioutil.NopCloser(bytes.NewReader(buf)), nil
	}
	req.Body, _ = req.GetBody()
	return req.Context(), nil
}

func (api *API) loadInstanceConfig(w http.ResponseWriter, r *http.Request) (context.Context, error) {
	ctx := r.Context()

	signature := r.Header.Get(jwsSignatureHeaderName)
	if signature == "" {
		return nil, badRequestError("Netlify microservice headers missing")
	}

	claims := NetlifyMicroserviceClaims{}
	p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}}
	_, err := p.ParseWithClaims(signature, &claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(api.config.NetlifySecret), nil
	})
	if err != nil {
		return nil, badRequestError("Netlify microservice headers are invalid: %v", err)
	}

	instanceID := claims.InstanceID
	if instanceID == "" {
		return nil, badRequestError("Instance ID is missing")
	}

	logEntrySetField(r, "instance_id", instanceID)
	instance, err := api.db.GetInstance(instanceID)
	if err != nil {
		if models.IsNotFoundError(err) {
			return nil, notFoundError("Unable to locate site configuration")
		}
		return nil, internalServerError("Database error loading instance").WithInternalError(err)
	}

	env := claims.Env
	if env == "" {
		return nil, badRequestError("No environment specified")
	}
	logEntrySetField(r, "env", env)
	config, err := instance.ConfigForEnvironment(env)
	if err != nil {
		return nil, internalServerError("Error loading environment config").WithInternalError(err)
	}

	if claims.SiteURL != "" {
		config.SiteURL = claims.SiteURL
	}
	logEntrySetField(r, "site_url", config.SiteURL)

	ctx, err = WithInstanceConfig(ctx, config, instanceID)
	if err != nil {
		return nil, internalServerError("Error loading instance config").WithInternalError(err)
	}

	return ctx, nil
}

func (api *API) verifyNetlifyRequest(w http.ResponseWriter, req *http.Request) (context.Context, error) {
	token := strings.TrimPrefix(req.Header.Get("authorization"), "Bearer ")
	if token != api.config.NetlifySecret {
		return nil, unauthorizedError("Request did not originate from Netlify")
	}
	return req.Context(), nil
}
<commit_msg>Add netlify_id to logger<commit_after>package api

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/netlify/gotrue/models"
)

const (
	jwsSignatureHeaderName = "x-nf-sign"
)

type NetlifyMicroserviceClaims struct {
	SiteURL    string `json:"site_url"`
	Env        string `json:"env"`
	InstanceID string `json:"id"`
	NetlifyID  string `json:"netlify_id"`
	jwt.StandardClaims
}

func addGetBody(w http.ResponseWriter, req *http.Request) (context.Context, error) {
	if req.Method == http.MethodGet {
		return req.Context(), nil
	}

	if req.Body == nil || req.Body == http.NoBody {
		return nil, badRequestError("request must provide a body")
	}

	buf, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, internalServerError("Error reading body").WithInternalError(err)
	}
	req.GetBody = func() (io.ReadCloser, error) {
		return ioutil.NopCloser(bytes.NewReader(buf)), nil
	}
	req.Body, _ = req.GetBody()
	return req.Context(), nil
}

func (api *API) loadInstanceConfig(w http.ResponseWriter, r *http.Request) (context.Context, error) {
	ctx := r.Context()

	signature := r.Header.Get(jwsSignatureHeaderName)
	if signature == "" {
		return nil, badRequestError("Netlify microservice headers missing")
	}

	claims := NetlifyMicroserviceClaims{}
	p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}}
	_, err := p.ParseWithClaims(signature, &claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(api.config.NetlifySecret), nil
	})
	if err != nil {
		return nil, badRequestError("Netlify microservice headers are invalid: %v", err)
	}

	instanceID := claims.InstanceID
	if instanceID == "" {
		return nil, badRequestError("Instance ID is missing")
	}

	logEntrySetField(r, "instance_id", instanceID)
	logEntrySetField(r, "netlify_id", claims.NetlifyID)
	instance, err := api.db.GetInstance(instanceID)
	if err != nil {
		if models.IsNotFoundError(err) {
			return nil, notFoundError("Unable to locate site configuration")
		}
		return nil, internalServerError("Database error loading instance").WithInternalError(err)
	}

	env := claims.Env
	if env == "" {
		return nil, badRequestError("No environment specified")
	}
	logEntrySetField(r, "env", env)
	config, err := instance.ConfigForEnvironment(env)
	if err != nil {
		return nil, internalServerError("Error loading environment config").WithInternalError(err)
	}

	if claims.SiteURL != "" {
		config.SiteURL = claims.SiteURL
	}
	logEntrySetField(r, "site_url", config.SiteURL)

	ctx, err = WithInstanceConfig(ctx, config, instanceID)
	if err != nil {
		return nil, internalServerError("Error loading instance config").WithInternalError(err)
	}

	return ctx, nil
}

func (api *API) verifyNetlifyRequest(w http.ResponseWriter, req *http.Request) (context.Context, error) {
	token := strings.TrimPrefix(req.Header.Get("authorization"), "Bearer ")
	if token != api.config.NetlifySecret {
		return nil, unauthorizedError("Request did not originate from Netlify")
	}
	return req.Context(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"
	"os"
	"time"

	"code.google.com/p/go.crypto/curve25519"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/scrypt"
	"code.google.com/p/goprotobuf/proto"
	"github.com/agl/pond/bbssig"
	"github.com/agl/pond/client/protos"
	pond "github.com/agl/pond/protos"
)

func (c *client) deriveKey(pw string) ([]byte, error) {
	return scrypt.Key([]byte(pw), c.diskSalt[:], 32768, 16, 1, 32)
}

func (c *client) save() {
	c.log.Printf("Saving state")
	serialized := c.marshal()
	c.writerChan <- serialized
}

func (c *client) unmarshal(state *protos.State) error {
	c.server = *state.Server

	if len(state.Identity) != len(c.identity) {
		return errors.New("client: identity is wrong length in State")
	}
	copy(c.identity[:], state.Identity)
	curve25519.ScalarBaseMult(&c.identityPublic, &c.identity)

	group, ok := new(bbssig.Group).Unmarshal(state.Group)
	if !ok {
		return errors.New("client: failed to unmarshal group")
	}
	c.groupPriv, ok = new(bbssig.PrivateKey).Unmarshal(group, state.GroupPrivate)
	if !ok {
		return errors.New("client: failed to unmarshal group private key")
	}

	if len(state.Private) != len(c.priv) {
		return errors.New("client: failed to unmarshal private key")
	}
	copy(c.priv[:], state.Private)
	if len(state.Public) != len(c.pub) {
		return errors.New("client: failed to unmarshal public key")
	}
	copy(c.pub[:], state.Public)
	c.generation = *state.Generation

	for _, cont := range state.Contacts {
		contact := &Contact{
			id:       *cont.Id,
			name:     *cont.Name,
			kxsBytes: cont.KeyExchangeBytes,
		}
		c.contacts[contact.id] = contact
		if contact.groupKey, ok = new(bbssig.MemberKey).Unmarshal(c.groupPriv.Group, cont.GroupKey); !ok {
			return errors.New("client: failed to unmarshal group member key")
		}
		copy(contact.lastDHPrivate[:], cont.LastPrivate)
		copy(contact.currentDHPrivate[:], cont.CurrentPrivate)
		if cont.IsPending != nil && *cont.IsPending {
			contact.isPending = true
			continue
		}

		theirGroup, ok := new(bbssig.Group).Unmarshal(cont.TheirGroup)
		if !ok {
			return errors.New("client: failed to unmarshal their group")
		}
		if contact.myGroupKey, ok = new(bbssig.MemberKey).Unmarshal(theirGroup, cont.MyGroupKey); !ok {
			return errors.New("client: failed to unmarshal my group key")
		}

		if cont.TheirServer == nil {
			return errors.New("client: contact missing server")
		}
		contact.theirServer = *cont.TheirServer

		if len(cont.TheirPub) != len(contact.theirPub) {
			return errors.New("client: contact missing public key")
		}
		copy(contact.theirPub[:], cont.TheirPub)

		if len(cont.TheirIdentityPublic) != len(contact.theirIdentityPublic) {
			return errors.New("client: contact missing identity public key")
		}
		copy(contact.theirIdentityPublic[:], cont.TheirIdentityPublic)

		copy(contact.theirLastDHPublic[:], cont.TheirLastPublic)
		copy(contact.theirCurrentDHPublic[:], cont.TheirCurrentPublic)
	}

	for _, m := range state.Inbox {
		msg := &InboxMessage{
			id:           *m.Id,
			from:         *m.From,
			receivedTime: time.Unix(*m.ReceivedTime, 0),
			acked:        *m.Acked,
			read:         *m.Read,
			sealed:       m.Sealed,
		}
		if len(m.Message) > 0 {
			msg.message = new(pond.Message)
			if err := proto.Unmarshal(m.Message, msg.message); err != nil {
				return errors.New("client: corrupt message in inbox: " + err.Error())
			}
		}

		c.inbox = append(c.inbox, msg)
	}

	for _, m := range state.Outbox {
		msg := &queuedMessage{
			id:      *m.Id,
			to:      *m.To,
			server:  *m.Server,
			created: time.Unix(*m.Created, 0),
		}
		msg.message = new(pond.Message)
		if err := proto.Unmarshal(m.Message, msg.message); err != nil {
			return errors.New("client: corrupt message in outbox: " + err.Error())
		}
		if m.Sent != nil {
			msg.sent = time.Unix(*m.Sent, 0)
		}
		if m.Acked != nil {
			msg.acked = time.Unix(*m.Acked, 0)
		}
		if len(m.Request) != 0 {
			msg.request = new(pond.Request)
			if err := proto.Unmarshal(m.Request, msg.request); err != nil {
				return errors.New("client: corrupt request in outbox: " + err.Error())
			}
		}

		c.outbox = append(c.outbox, msg)

		if msg.sent.IsZero() {
			// This message hasn't been sent yet.
			c.enqueue(msg)
		}
	}

	return nil
}

func (c *client) marshal() []byte {
	var err error
	var contacts []*protos.Contact

	for _, contact := range c.contacts {
		cont := &protos.Contact{
			Id:               proto.Uint64(contact.id),
			Name:             proto.String(contact.name),
			GroupKey:         contact.groupKey.Marshal(),
			IsPending:        proto.Bool(contact.isPending),
			KeyExchangeBytes: contact.kxsBytes,
			LastPrivate:      contact.lastDHPrivate[:],
			CurrentPrivate:   contact.currentDHPrivate[:],
		}
		if !contact.isPending {
			cont.MyGroupKey = contact.myGroupKey.Marshal()
			cont.TheirGroup = contact.myGroupKey.Group.Marshal()
			cont.TheirServer = proto.String(contact.theirServer)
			cont.TheirPub = contact.theirPub[:]
			cont.TheirIdentityPublic = contact.theirIdentityPublic[:]
			cont.TheirLastPublic = contact.theirLastDHPublic[:]
			cont.TheirCurrentPublic = contact.theirCurrentDHPublic[:]
		}
		contacts = append(contacts, cont)
	}

	var inbox []*protos.Inbox
	for _, msg := range c.inbox {
		if time.Since(msg.receivedTime) > messageLifetime {
			continue
		}
		m := &protos.Inbox{
			Id:           proto.Uint64(msg.id),
			From:         proto.Uint64(msg.from),
			ReceivedTime: proto.Int64(msg.receivedTime.Unix()),
			Acked:        proto.Bool(msg.acked),
			Read:         proto.Bool(msg.read),
			Sealed:       msg.sealed,
		}
		if msg.message != nil {
			if m.Message, err = proto.Marshal(msg.message); err != nil {
				panic(err)
			}
		}
		inbox = append(inbox, m)
	}

	var outbox []*protos.Outbox
	for _, msg := range c.outbox {
		if time.Since(msg.created) > messageLifetime {
			continue
		}
		m := &protos.Outbox{
			Id:      proto.Uint64(msg.id),
			To:      proto.Uint64(msg.to),
			Server:  proto.String(msg.server),
			Created: proto.Int64(msg.created.Unix()),
		}
		if m.Message, err = proto.Marshal(msg.message); err != nil {
			panic(err)
		}
		if !msg.sent.IsZero() {
			m.Sent = proto.Int64(msg.sent.Unix())
		}
		if !msg.acked.IsZero() {
			m.Acked = proto.Int64(msg.acked.Unix())
		}
		if msg.request != nil {
			if m.Request, err = proto.Marshal(msg.request); err != nil {
				panic(err)
			}
		}

		outbox = append(outbox, m)
	}

	state := &protos.State{
		Private:      c.priv[:],
		Public:       c.pub[:],
		Identity:     c.identity[:],
		Server:       proto.String(c.server),
		Group:        c.groupPriv.Group.Marshal(),
		GroupPrivate: c.groupPriv.Marshal(),
		Generation:   proto.Uint32(c.generation),
		Contacts:     contacts,
		Inbox:        inbox,
		Outbox:       outbox,
	}
	s, err := proto.Marshal(state)
	if err != nil {
		panic(err)
	}
	return s
}

const sCryptSaltLen = 32
const diskSaltLen = 32
const smearedCopies = 32768 / 24

func getSCryptSaltFromState(state []byte) ([32]byte, bool) {
	var salt [32]byte
	if len(state) < sCryptSaltLen {
		return salt, false
	}
	copy(salt[:], state)
	return salt, true
}

func stateWriter(stateFilename string, key *[32]byte, salt *[sCryptSaltLen]byte, states chan []byte, done chan bool) {
	for {
		s, ok := <-states
		if !ok {
			close(done)
			return
		}

		length := uint32(len(s)) + 4
		for i := uint(17); i < 32; i++ {
			if n := (uint32(1) << i); n >= length {
				length = n
				break
			}
		}

		plaintext := make([]byte, length)
		copy(plaintext[4:], s)
		if _, err := io.ReadFull(rand.Reader, plaintext[len(s)+4:]); err != nil {
			panic(err)
		}
		binary.LittleEndian.PutUint32(plaintext, uint32(len(s)))

		var nonceSmear [24 * smearedCopies]byte
		if _, err := io.ReadFull(rand.Reader, nonceSmear[:]); err != nil {
			panic(err)
		}

		var nonce [24]byte
		for i := 0; i < smearedCopies; i++ {
			for j := 0; j < 24; j++ {
				nonce[j] ^= nonceSmear[24*i+j]
			}
		}

		ciphertext := secretbox.Seal(nil, plaintext, &nonce, key)

		out, err := os.Create(stateFilename)
		if err != nil {
			panic(err)
		}
		if _, err := out.Write(salt[:]); err != nil {
			panic(err)
		}
		if _, err := out.Write(nonceSmear[:]); err != nil {
			panic(err)
		}
		if _, err := out.Write(ciphertext); err != nil {
			panic(err)
		}
		out.Close()
	}
}

var badPasswordError = errors.New("bad password")

func (c *client) loadState(b []byte, key *[32]byte) error {
	if len(b) < sCryptSaltLen+24*smearedCopies {
		return errors.New("state file is too small to be valid")
	}

	b = b[sCryptSaltLen:]

	var nonce [24]byte
	for i := 0; i < smearedCopies; i++ {
		for j := 0; j < 24; j++ {
			nonce[j] ^= b[24*i+j]
		}
	}

	b = b[24*smearedCopies:]
	plaintext, ok := secretbox.Open(nil, b, &nonce, key)
	if !ok {
		return badPasswordError
	}
	if len(plaintext) < 4 {
		return errors.New("state file corrupt")
	}
	length := binary.LittleEndian.Uint32(plaintext[:4])
	plaintext = plaintext[4:]
	if length > 1<<31 || length > uint32(len(plaintext)) {
		return errors.New("state file corrupt")
	}
	plaintext = plaintext[:int(length)]

	var state protos.State
	if err := proto.Unmarshal(plaintext, &state); err != nil {
		return err
	}

	if err := c.unmarshal(&state); err != nil {
		return err
	}
	return nil
}
<commit_msg>Save group generation number for contacts.<commit_after>package main

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"
	"os"
	"time"

	"code.google.com/p/go.crypto/curve25519"
	"code.google.com/p/go.crypto/nacl/secretbox"
	"code.google.com/p/go.crypto/scrypt"
	"code.google.com/p/goprotobuf/proto"
	"github.com/agl/pond/bbssig"
	"github.com/agl/pond/client/protos"
	pond "github.com/agl/pond/protos"
)

func (c *client) deriveKey(pw string) ([]byte, error) {
	return scrypt.Key([]byte(pw), c.diskSalt[:], 32768, 16, 1, 32)
}

func (c *client) save() {
	c.log.Printf("Saving state")
	serialized := c.marshal()
	c.writerChan <- serialized
}

func (c *client) unmarshal(state *protos.State) error {
	c.server = *state.Server

	if len(state.Identity) != len(c.identity) {
		return errors.New("client: identity is wrong length in State")
	}
	copy(c.identity[:], state.Identity)
	curve25519.ScalarBaseMult(&c.identityPublic, &c.identity)

	group, ok := new(bbssig.Group).Unmarshal(state.Group)
	if !ok {
		return errors.New("client: failed to unmarshal group")
	}
	c.groupPriv, ok = new(bbssig.PrivateKey).Unmarshal(group, state.GroupPrivate)
	if !ok {
		return errors.New("client: failed to unmarshal group private key")
	}

	if len(state.Private) != len(c.priv) {
		return errors.New("client: failed to unmarshal private key")
	}
	copy(c.priv[:], state.Private)
	if len(state.Public) != len(c.pub) {
		return errors.New("client: failed to unmarshal public key")
	}
	copy(c.pub[:], state.Public)
	c.generation = *state.Generation

	for _, cont := range state.Contacts {
		contact := &Contact{
			id:       *cont.Id,
			name:     *cont.Name,
			kxsBytes: cont.KeyExchangeBytes,
		}
		c.contacts[contact.id] = contact
		if contact.groupKey, ok = new(bbssig.MemberKey).Unmarshal(c.groupPriv.Group, cont.GroupKey); !ok {
			return errors.New("client: failed to unmarshal group member key")
		}
		copy(contact.lastDHPrivate[:], cont.LastPrivate)
		copy(contact.currentDHPrivate[:], cont.CurrentPrivate)
		if cont.IsPending != nil && *cont.IsPending {
			contact.isPending = true
			continue
		}

		theirGroup, ok := new(bbssig.Group).Unmarshal(cont.TheirGroup)
		if !ok {
			return errors.New("client: failed to unmarshal their group")
		}
		if contact.myGroupKey, ok = new(bbssig.MemberKey).Unmarshal(theirGroup, cont.MyGroupKey); !ok {
			return errors.New("client: failed to unmarshal my group key")
		}

		if cont.TheirServer == nil {
			return errors.New("client: contact missing server")
		}
		contact.theirServer = *cont.TheirServer

		if len(cont.TheirPub) != len(contact.theirPub) {
			return errors.New("client: contact missing public key")
		}
		copy(contact.theirPub[:], cont.TheirPub)

		if len(cont.TheirIdentityPublic) != len(contact.theirIdentityPublic) {
			return errors.New("client: contact missing identity public key")
		}
		copy(contact.theirIdentityPublic[:], cont.TheirIdentityPublic)

		copy(contact.theirLastDHPublic[:], cont.TheirLastPublic)
		copy(contact.theirCurrentDHPublic[:], cont.TheirCurrentPublic)

		contact.generation = *cont.Generation
	}

	for _, m := range state.Inbox {
		msg := &InboxMessage{
			id:           *m.Id,
			from:         *m.From,
			receivedTime: time.Unix(*m.ReceivedTime, 0),
			acked:        *m.Acked,
			read:         *m.Read,
			sealed:       m.Sealed,
		}
		if len(m.Message) > 0 {
			msg.message = new(pond.Message)
			if err := proto.Unmarshal(m.Message, msg.message); err != nil {
				return errors.New("client: corrupt message in inbox: " + err.Error())
			}
		}

		c.inbox = append(c.inbox, msg)
	}

	for _, m := range state.Outbox {
		msg := &queuedMessage{
			id:      *m.Id,
			to:      *m.To,
			server:  *m.Server,
			created: time.Unix(*m.Created, 0),
		}
		msg.message = new(pond.Message)
		if err := proto.Unmarshal(m.Message, msg.message); err != nil {
			return errors.New("client: corrupt message in outbox: " + err.Error())
		}
		if m.Sent != nil {
			msg.sent = time.Unix(*m.Sent, 0)
		}
		if m.Acked != nil {
			msg.acked = time.Unix(*m.Acked, 0)
		}
		if len(m.Request) != 0 {
			msg.request = new(pond.Request)
			if err := proto.Unmarshal(m.Request, msg.request); err != nil {
				return errors.New("client: corrupt request in outbox: " + err.Error())
			}
		}

		c.outbox = append(c.outbox, msg)

		if msg.sent.IsZero() {
			// This message hasn't been sent yet.
			c.enqueue(msg)
		}
	}

	return nil
}

func (c *client) marshal() []byte {
	var err error
	var contacts []*protos.Contact

	for _, contact := range c.contacts {
		cont := &protos.Contact{
			Id:               proto.Uint64(contact.id),
			Name:             proto.String(contact.name),
			GroupKey:         contact.groupKey.Marshal(),
			IsPending:        proto.Bool(contact.isPending),
			KeyExchangeBytes: contact.kxsBytes,
			LastPrivate:      contact.lastDHPrivate[:],
			CurrentPrivate:   contact.currentDHPrivate[:],
		}
		if !contact.isPending {
			cont.MyGroupKey = contact.myGroupKey.Marshal()
			cont.TheirGroup = contact.myGroupKey.Group.Marshal()
			cont.TheirServer = proto.String(contact.theirServer)
			cont.TheirPub = contact.theirPub[:]
			cont.TheirIdentityPublic = contact.theirIdentityPublic[:]
			cont.TheirLastPublic = contact.theirLastDHPublic[:]
			cont.TheirCurrentPublic = contact.theirCurrentDHPublic[:]
			cont.Generation = proto.Uint32(contact.generation)
		}
		contacts = append(contacts, cont)
	}

	var inbox []*protos.Inbox
	for _, msg := range c.inbox {
		if time.Since(msg.receivedTime) > messageLifetime {
			continue
		}
		m := &protos.Inbox{
			Id:           proto.Uint64(msg.id),
			From:         proto.Uint64(msg.from),
			ReceivedTime: proto.Int64(msg.receivedTime.Unix()),
			Acked:        proto.Bool(msg.acked),
			Read:         proto.Bool(msg.read),
			Sealed:       msg.sealed,
		}
		if msg.message != nil {
			if m.Message, err = proto.Marshal(msg.message); err != nil {
				panic(err)
			}
		}
		inbox = append(inbox, m)
	}

	var outbox []*protos.Outbox
	for _, msg := range c.outbox {
		if time.Since(msg.created) > messageLifetime {
			continue
		}
		m := &protos.Outbox{
			Id:      proto.Uint64(msg.id),
			To:      proto.Uint64(msg.to),
			Server:  proto.String(msg.server),
			Created: proto.Int64(msg.created.Unix()),
		}
		if m.Message, err = proto.Marshal(msg.message); err != nil {
			panic(err)
		}
		if !msg.sent.IsZero() {
			m.Sent = proto.Int64(msg.sent.Unix())
		}
		if !msg.acked.IsZero() {
			m.Acked = proto.Int64(msg.acked.Unix())
		}
		if msg.request != nil {
			if m.Request, err = proto.Marshal(msg.request); err != nil {
				panic(err)
			}
		}

		outbox = append(outbox, m)
	}

	state := &protos.State{
		Private:      c.priv[:],
		Public:       c.pub[:],
		Identity:     c.identity[:],
		Server:       proto.String(c.server),
		Group:        c.groupPriv.Group.Marshal(),
		GroupPrivate: c.groupPriv.Marshal(),
		Generation:   proto.Uint32(c.generation),
		Contacts:     contacts,
		Inbox:        inbox,
		Outbox:       outbox,
	}
	s, err := proto.Marshal(state)
	if err != nil {
		panic(err)
	}
	return s
}

const sCryptSaltLen = 32
const diskSaltLen = 32
const smearedCopies = 32768 / 24

func getSCryptSaltFromState(state []byte) ([32]byte, bool) {
	var salt [32]byte
	if len(state) < sCryptSaltLen {
		return salt, false
	}
	copy(salt[:], state)
	return salt, true
}

func stateWriter(stateFilename string, key *[32]byte, salt *[sCryptSaltLen]byte, states chan []byte, done chan bool) {
	for {
		s, ok := <-states
		if !ok {
			close(done)
			return
		}

		length := uint32(len(s)) + 4
		for i := uint(17); i < 32; i++ {
			if n := (uint32(1) << i); n >= length {
				length = n
				break
			}
		}

		plaintext := make([]byte, length)
		copy(plaintext[4:], s)
		if _, err := io.ReadFull(rand.Reader, plaintext[len(s)+4:]); err != nil {
			panic(err)
		}
		binary.LittleEndian.PutUint32(plaintext, uint32(len(s)))

		var nonceSmear [24 * smearedCopies]byte
		if _, err := io.ReadFull(rand.Reader, nonceSmear[:]); err != nil {
			panic(err)
		}

		var nonce [24]byte
		for i := 0; i < smearedCopies; i++ {
			for j := 0; j < 24; j++ {
				nonce[j] ^= nonceSmear[24*i+j]
			}
		}

		ciphertext := secretbox.Seal(nil, plaintext, &nonce, key)

		out, err := os.Create(stateFilename)
		if err != nil {
			panic(err)
		}
		if _, err := out.Write(salt[:]); err != nil {
			panic(err)
		}
		if _, err := out.Write(nonceSmear[:]); err != nil {
			panic(err)
		}
		if _, err := out.Write(ciphertext); err != nil {
			panic(err)
		}
		out.Close()
	}
}

var badPasswordError = errors.New("bad password")

func (c *client) loadState(b []byte, key *[32]byte) error {
	if len(b) < sCryptSaltLen+24*smearedCopies {
		return errors.New("state file is too small to be valid")
	}

	b = b[sCryptSaltLen:]

	var nonce [24]byte
	for i := 0; i < smearedCopies; i++ {
		for j := 0; j < 24; j++ {
			nonce[j] ^= b[24*i+j]
		}
	}

	b = b[24*smearedCopies:]
	plaintext, ok := secretbox.Open(nil, b, &nonce, key)
	if !ok {
		return badPasswordError
	}
	if len(plaintext) < 4 {
		return errors.New("state file corrupt")
	}
	length := binary.LittleEndian.Uint32(plaintext[:4])
	plaintext = plaintext[4:]
	if length > 1<<31 || length > uint32(len(plaintext)) {
		return errors.New("state file corrupt")
	}
	plaintext = plaintext[:int(length)]

	var state protos.State
	if err := proto.Unmarshal(plaintext, &state); err != nil {
		return err
	}

	if err := c.unmarshal(&state); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, 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 api

import (
	"encoding/json"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/auth"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err := ra.GetInt64("project_id")
	if err != nil {
		log.Errorf("Failed to get project id, error: %v", err)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}

	if p.Public == 0 {
		userID := ra.ValidateUser()

		if !checkProjectPermission(userID, projectID) {
			ra.RenderError(http.StatusForbidden, "")
			return
		}
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

type tag struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	sort.Strings(tags)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
	endpoint := os.Getenv("REGISTRY_URL")

	username, password, ok := ra.Ctx.Request.BasicAuth()
	if ok {
		credential := auth.NewBasicAuthCredential(username, password)
		return registry.NewRepositoryWithCredential(repoName, endpoint, credential)
	}

	username, err = ra.getUsername()
	if err != nil {
		return nil, err
	}

	return registry.NewRepositoryWithUsername(repoName, endpoint, username)
}

func (ra *RepositoryAPI) getUsername() (string, error) {
	// get username from session
	sessionUsername := ra.GetSession("username")
	if sessionUsername != nil {
		username, ok := sessionUsername.(string)
		if ok {
			return username, nil
		}
	}

	// if username does not exist in session, try to get userId from sessiion
	// and then get username from DB according to the userId
	sessionUserID := ra.GetSession("userId")
	if sessionUserID != nil {
		userID, ok := sessionUserID.(int)
		if ok {
			u := models.User{
				UserID: userID,
			}
			user, err := dao.GetUser(u)
			if err != nil {
				return "", err
			}

			return user.Username, nil
		}
	}

	return "", nil
}

//getTopTenRepos handles request GET /api/repositories/toprepos
func (ra *RepositoryAPI) GetTopTenRepos() {
	repos, err := dao.GetTop10Repos()
	if err != nil {
		log.Errorf("error occured in get top 10 repos: v%", err)
		ra.CustomAbort(http.StatusInternalServerError, "internal server error")
	}
	ra.Data["json"] = repos
	ra.ServeJSON()
}
<commit_msg>change code according to travis CI<commit_after>/*
   Copyright (c) 2016 VMware, 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 api

import (
	"encoding/json"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/auth"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err := ra.GetInt64("project_id")
	if err != nil {
		log.Errorf("Failed to get project id, error: %v", err)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}

	if p.Public == 0 {
		userID := ra.ValidateUser()

		if !checkProjectPermission(userID, projectID) {
			ra.RenderError(http.StatusForbidden, "")
			return
		}
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

type tag struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	sort.Strings(tags)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
	endpoint := os.Getenv("REGISTRY_URL")

	username, password, ok := ra.Ctx.Request.BasicAuth()
	if ok {
		credential := auth.NewBasicAuthCredential(username, password)
		return registry.NewRepositoryWithCredential(repoName, endpoint, credential)
	}

	username, err = ra.getUsername()
	if err != nil {
		return nil, err
	}

	return registry.NewRepositoryWithUsername(repoName, endpoint, username)
}

func (ra *RepositoryAPI) getUsername() (string, error) {
	// get username from session
	sessionUsername := ra.GetSession("username")
	if sessionUsername != nil {
		username, ok := sessionUsername.(string)
		if ok {
			return username, nil
		}
	}

	// if username does not exist in session, try to get userId from sessiion
	// and then get username from DB according to the userId
	sessionUserID := ra.GetSession("userId")
	if sessionUserID != nil {
		userID, ok := sessionUserID.(int)
		if ok {
			u := models.User{
				UserID: userID,
			}
			user, err := dao.GetUser(u)
			if err != nil {
				return "", err
			}

			return user.Username, nil
		}
	}

	return "", nil
}

//GetTopTenRepos handles request GET /api/repositories/toprepos
func (ra *RepositoryAPI) GetTopTenRepos() {
	repos, err := dao.GetTop10Repos()
	if err != nil {
		log.Errorf("error occured in get top 10 repos: v%", err)
		ra.CustomAbort(http.StatusInternalServerError, "internal server error")
	}
	ra.Data["json"] = repos
	ra.ServeJSON()
}
<|endoftext|>