#include "TLSServer.h"
#include "TLSSession.h"
#include "EPoll.h"
#include "TCPSession.h"
#include "Exception.h"

namespace core {
   
   static pthread_mutex_t *lockarray;

   static unsigned long thread_id(void) {	
      return ((unsigned long) pthread_self());
   }
   
   static void lock_callback(int mode, int type, const char *file, int line) {
      if(mode & CRYPTO_LOCK)  
        pthread_mutex_lock(&(lockarray[type]));   
      else 
        pthread_mutex_unlock(&(lockarray[type]));
   }
   
   TLSServer::TLSServer(EPoll &ePoll, IPAddress address) : TCPServer(ePoll, address) {

      SSL_library_init();
      SSL_load_error_strings();
      
      lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
      for(int i = 0; i < CRYPTO_num_locks(); ++i) 
      pthread_mutex_init(&(lockarray[i]), NULL);
      
      CRYPTO_set_id_callback((unsigned long (*)())thread_id);
      CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback);
      
      SSLeay_add_ssl_algorithms();
      RAND_load_file("/dev/hwrng", 1024);   

      if(!(ctx = SSL_CTX_new(SSLv23_server_method()))) 
        throw coreutils::Exception("Error while setting server method SSLv23.");     
      SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
      SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
      SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
      //   SSL_CTX_set_generate_session_id(ctx, generate_session_id);
      SSL_CTX_set_cipher_list(ctx, "ECDH-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA");
      if(SSL_CTX_use_certificate_file(ctx, sip_cert, SSL_FILETYPE_PEM) <= 0) 
        throw coreutils::Exception("Error looking up certificate.");
      if(SSL_CTX_use_PrivateKey_file(ctx, sip_key, SSL_FILETYPE_PEM) < 0)
        throw coreutils::Exception("Error with private key.");
      if(SSL_CTX_check_private_key(ctx) != 1)
        throw coreutils::Exception("Private key does not match certificate.");
      SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
      SSL_CTX_set_verify_depth(ctx, 1);
      if(!SSL_CTX_load_verify_locations(ctx, sip_cacert, NULL))
        throw coreutils::Exception("Cannot verify locations.");
      SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(sip_cacert));       
      coreutils::Log(coreutils::LOG_DEBUG_1) << "Server key authenticated.";
   }
   
   TLSServer::~TLSServer() {
      
   }
   
   TCPSession * TLSServer::getSocketAccept() {
      return new TLSSession(ePoll, *this);
   }
   
}
