use strict;
package Business::OnlinePayment::PPIPayMover::SecureHttp;
use Socket;
use Net::SSLeay qw(die_now die_if_ssl_error) ;
1;

# constuctor
sub new
{
  my $class = shift;
  my $self = {};
  bless $self, $class;
  $self->{ctx} = undef;
  $self->{ssl} = undef;
  $self->{strError} = ""; 
  return $self;
}

sub Init
{
  my $self = shift;  
  
  Net::SSLeay::load_error_strings();
  Net::SSLeay::ERR_load_crypto_strings();
  Net::SSLeay::SSLeay_add_ssl_algorithms();
  Net::SSLeay::randomize();
  
  $self->{ctx} = Net::SSLeay::CTX_new();
  if(!$self->{ctx}) {
      $self->{strError} .= "Failed to create SSL_CTX. \n" .
         "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error);
      return 0;
  }
  
  if(!Net::SSLeay::CTX_set_options($self->{ctx}, &Net::SSLeay::OP_ALL)) {
      # For some reason the if statement above always returns false,
      # but SSLeay reports no error.  Ignore this error, since
      # everything still works fine.
      #
      #$self->{strError} .= "Failed to set SSL_CTX options. \n" .
      #   "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error) . "\n";
  }
  
  $self->{ssl} = Net::SSLeay::new($self->{ctx});
  if(!$self->{ssl}) {
      $self->{strError} .= "Failed to create an SSL. \n" .
         "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error);
      return 0;
  }
  
  return 1;
}

sub Connect
{
  my $self = shift;
  my ($destServer, $port) = @_;
  $port = getservbyname($port, 'tcp') unless $port =~ /^\d+$/;
  
  my $destIp = gethostbyname ($destServer);
  if(!defined($destIp)) { 
      $self->{strError} .= "Couldn't resolve host name (gethostbyname) using host: $destServer\n";
      return 0;
  } 
  
  my $destServerSockAddr = sockaddr_in($port, $destIp);
  
  if(!socket (S, AF_INET, SOCK_STREAM, 0)) {
      $self->{strError} .= "Failed to create a socket. $!";
      return 0;
  }
  
  if(!connect (S, $destServerSockAddr)) {
      $self->{strError} .= "Failed to connect. $!";
      return 0;
  }
  
  select (S); $| = 1; select (STDOUT);   # Eliminate STDIO buffering
  Net::SSLeay::set_fd($self->{ssl}, fileno(S));   # Must use fileno
  if (! Net::SSLeay::connect($self->{ssl})) {
    $self->{strError} .= "Failed to make an ssl connect. \n" .
      "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error);
     return 0;
  }
  
  return 1;
}

sub DoSecurePost
{
  my $self = shift;
  my ($strPath, $strContent, $Response) = @_;
  my $PostString = "POST ";
  $PostString .= $strPath;
  $PostString .= " HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\n";
  $PostString .= "Content-Length: ";
  $PostString .= length($strContent);
  $PostString .= "  \r\n\r\n";
  $PostString .= $strContent;
  
  if(!Net::SSLeay::ssl_write_all($self->{ssl}, $PostString)) {
      $self->{strError} .= "Failed to write. " .
         "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error);
    return 0;
  }
  
  shutdown S, 1;  # Half close --> No more output, sends EOF to server

  if( $^O eq "MSWin32" ) {
    # Windows doesn't implement ALRM signal, 
    # so don't use a timeout.  
    # May hang client system.
    $$Response = Net::SSLeay::ssl_read_all($self->{ssl});
  } else {
    # This block uses the alarm signal
    # to see if the server times out responding.
    eval {
      local $SIG{ ALRM } = sub {
        $self->{strError} .= "Server timed out.";
        close S;
      };
      alarm 270;    # Alarm on 4.5 min timeout
      # Read in response from server
      $$Response = Net::SSLeay::ssl_read_all($self->{ssl});
    };
    alarm 0;    # Alarm off
    
  }

  if ( !defined( $$Response ) ) {
    $self->{strError} .= "Failed to read from socket. " .
         "SSLeay error: " . Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error);
    return 0;
  }
  return 1;
}

sub DisconnectFromServer
{
  my $self = shift;
  Net::SSLeay::free ($self->{ssl});               # Tear down connection
  Net::SSLeay::CTX_free ($self->{ctx});
  close S;
}

sub CleanUp
{
  return 1;
}

sub GetErrorString
{
  my $self = shift;
  return $self->{strError};
}
