package io.tetrapod.core;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.net.ssl.*;

import org.slf4j.*;

/**
 * A server that speaks the tetrapod wire protocol
 */
public class Server extends ChannelInitializer<SocketChannel> implements Session.Listener {

   public static final Logger    logger   = LoggerFactory.getLogger(Server.class);

   private Map<Integer, Session> sessions = new ConcurrentHashMap<>();

   private EventLoopGroup        bossGroup;

   private int                   port;
   private final Dispatcher      dispatcher;
   private final SessionFactory  sessionFactory;
   private SSLContext            sslContext;
   private boolean               clientAuth;

   public Server(int port, SessionFactory sessionFactory, Dispatcher dispatcher) {
      this.sessionFactory = sessionFactory;
      this.dispatcher = dispatcher;
      this.port = port;
   }

   public Server(int port, SessionFactory sessionFactory, Dispatcher dispatcher, SSLContext ctx, boolean clientAuth) {
      this(port, sessionFactory, dispatcher);
      enableTLS(ctx, clientAuth);
   }

   public void enableTLS(SSLContext ctx, boolean clientAuth) {
      this.sslContext = ctx;
      this.clientAuth = clientAuth;
   }

   public synchronized ChannelFuture start(int port) {
      this.port = port;
      return start();
   }

   public synchronized ChannelFuture start() {
      bossGroup = dispatcher.getBossGroup();
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, dispatcher.getWorkerGroup()).channel(NioServerSocketChannel.class);
      b.childHandler(this);
      setOptions(b);
      logger.info("Starting Server Listening on Port {}", port);
      return b.bind(port);
   }

   public void initChannel(SocketChannel ch) throws Exception {
      startSession(ch);
   }

   protected void setOptions(ServerBootstrap sb) {
      sb.option(ChannelOption.SO_BACKLOG, 128);
      sb.childOption(ChannelOption.SO_KEEPALIVE, true);
   }

   public void stop() {
      logger.debug("Stopping server on port {}...", port);
      try {
         bossGroup.shutdownGracefully().sync();
      } catch (Exception e) {
         logger.error(e.getMessage(), e);
      }
      logger.debug("Stopped server on port {}", port);
   }

   private void startSession(final SocketChannel ch) throws Exception {
      logger.info("Connection from {}", ch);
      if (sslContext != null) {
         SSLEngine engine = sslContext.createSSLEngine();
         engine.setUseClientMode(false);
         engine.setWantClientAuth(clientAuth);

         // Explicitly removes "SSLv3" from supported protocols to prevent the 'POODLE' exploit
         final List<String> list = new ArrayList<>();
         for (String p : engine.getEnabledProtocols()) {
            if (!p.equals("SSLv3")) {
               list.add(p);
            }
         }
         engine.setEnabledProtocols(list.toArray(new String[list.size()]));

         SslHandler handler = new SslHandler(engine);
         ch.pipeline().addLast("ssl", handler);
      }
      Session session = sessionFactory.makeSession(ch);
      session.addSessionListener(this);
   }

   @Override
   public void onSessionStart(Session ses) {
      sessions.put(ses.getSessionNum(), ses);
   }

   @Override
   public void onSessionStop(Session ses) {
      sessions.remove(ses.getSessionNum());
   }

   public synchronized int getPort() {
      return port;
   }

   public int getNumSessions() {
      return sessions.size();
   }
}

@@ -2482,24 +2482,580 @@
 ort);%0A   %7D%0A%0A
+   private static final Set%3CString%3E WEAK_CIPHERS = new HashSet%3C%3E();%0A   static %7B%0A      WEAK_CIPHERS.add(%22SSL_RSA_WITH_RC4_128_MD5%22);%0A      WEAK_CIPHERS.add(%22SSL_RSA_WITH_RC4_128_SHA%22);%0A      WEAK_CIPHERS.add(%22SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA%22);%0A      WEAK_CIPHERS.add(%22TLS_ECDHE_RSA_WITH_RC4_128_SHA%22);%0A      WEAK_CIPHERS.add(%22TLS_DHE_RSA_WITH_AES_128_CBC_SHA%22);%0A      WEAK_CIPHERS.add(%22TLS_DHE_RSA_WITH_AES_128_CBC_SHA256%22);%0A      WEAK_CIPHERS.add(%22TLS_DHE_RSA_WITH_AES_256_CBC_SHA%22);%0A      WEAK_CIPHERS.add(%22TLS_DHE_RSA_WITH_AES_256_CBC_SHA256%22);%0A   %7D;%0A%0A
    private v
@@ -3462,20 +3462,25 @@
 String%3E 
-list
+protocols
  = new A
@@ -3603,20 +3603,25 @@
         
-list
+protocols
 .add(p);
@@ -3682,20 +3682,25 @@
 otocols(
-list
+protocols
 .toArray
@@ -3715,12 +3715,466 @@
 ing%5B
-list
+protocols.size()%5D));%0A%0A         // remove weak cipher suites from the enabled list%0A         final List%3CString%3E ciphers = new ArrayList%3C%3E();%0A         for (String p : engine.getEnabledCipherSuites()) %7B%0A            if (!WEAK_CIPHERS.contains(p)) %7B%0A               ciphers.add(p);%0A            %7D else %7B%0A               logger.trace(%22removing weak cipher suit %7B%7D%22, p);%0A            %7D%0A         %7D%0A         engine.setEnabledCipherSuites(ciphers.toArray(new String%5Bciphers
 .siz

remove known weak cipher suites from our ssl engine