package server

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net"
	"os"

	"github.com/docker/docker/engine"
	"github.com/docker/docker/pkg/listenbuffer"
)

type tlsConfig struct {
	CA          string
	Certificate string
	Key         string
	Verify      bool
}

func tlsConfigFromJob(job *engine.Job) *tlsConfig {
	verify := job.GetenvBool("TlsVerify")
	if !job.GetenvBool("Tls") && !verify {
		return nil
	}
	return &tlsConfig{
		Verify:      verify,
		Certificate: job.Getenv("TlsCert"),
		Key:         job.Getenv("TlsKey"),
		CA:          job.Getenv("TlsCa"),
	}
}

func NewTcpSocket(addr string, config *tlsConfig) (net.Listener, error) {
	l, err := listenbuffer.NewListenBuffer("tcp", addr, activationLock)
	if err != nil {
		return nil, err
	}
	if config != nil {
		if l, err = setupTls(l, config); err != nil {
			return nil, err
		}
	}
	return l, nil
}

func setupTls(l net.Listener, config *tlsConfig) (net.Listener, error) {
	tlsCert, err := tls.LoadX509KeyPair(config.Certificate, config.Key)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("Could not load X509 key pair (%s, %s): %v", config.Certificate, config.Key, err)
		}
		return nil, fmt.Errorf("Error reading X509 key pair (%s, %s): %q. Make sure the key is encrypted.",
			config.Certificate, config.Key, err)
	}
	tlsConfig := &tls.Config{
		NextProtos:   []string{"http/1.1"},
		Certificates: []tls.Certificate{tlsCert},
		// Avoid fallback on insecure SSL protocols
		MinVersion: tls.VersionTLS10,
	}
	if config.CA != "" {
		certPool := x509.NewCertPool()
		file, err := ioutil.ReadFile(config.CA)
		if err != nil {
			return nil, fmt.Errorf("Could not read CA certificate: %v", err)
		}
		certPool.AppendCertsFromPEM(file)
		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
		tlsConfig.ClientCAs = certPool
	}
	return tls.NewListener(l, tlsConfig), nil
}