The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package RWDE::PostMaster;

use strict;
use warnings;

use base qw( RWDE::Singleton );

use Template;

use Net::SMTP;

use RWDE::Configuration;
use RWDE::RObject;

my $unique_instance;

use vars qw($VERSION);
$VERSION = sprintf "%d", q$Revision: 527 $ =~ /(\d+)/;

=pod

=head1 RWDE::Postmaster

This class implements methods that detail the sending of email. Both verp and standard emailing are implemented here, as 
well as methods for automatically reporting exceptions and hooks for assigning tickets to RT.

=cut

=head2 get_instance()

Return an instance of RWDE::Postmaster

=cut

sub get_instance {
  my ($self, $params) = @_;

  if (ref $unique_instance ne $self) {
    $unique_instance = $self->new;
  }

  return $unique_instance;
}

=head2 initialize()

Initialize an instance of RWDE::Postmaster. This includes pulling some data from the config file in order to find an
SMTP server and preparing to handle a mail template.

=cut

sub initialize {
  my ($self, $params) = @_;

  $self->{server} = RWDE::Configuration->get_SMTP();

  # create template object for future use
  $self->{template} = Template->new(
    {
      TAG_STYLE    => 'asp',
      PROCESS      => 'message.tt',
      INCLUDE_PATH => RWDE::Configuration->get_root . '/templates/emailmessages',
      VARIABLES    => {
        commify => \&RWDE::Utility::commify,
        global  => RWDE::Configuration->get_instance,
      },
    }
  ) or throw RWDE::DevelException({ info => 'Template::new failure.' });

  return ();
}

=head2 send_message ($smtp_sender, $smtp_recipient, $template)

Prepare or send a 1-to-1 message to the $smtp_recipient address, from $smtp_sender address, using $template as a template input
and the $params to populate the template. To alter the header from/to etc, edit the template.

=cut

sub send_message {
  my ($self, $params) = @_;

  my @required = qw( smtp_sender smtp_recipient template );
  RWDE::RObject->check_params({ required => \@required, supplied => $params });

  # Process the message thru the Template
  my $output;

  my $postmaster = RWDE::PostMaster->get_instance();

  my $template = $postmaster->{template};

  unless ($template->process($$params{template}, $params, \$output)) {
    throw RWDE::DevelException({ info => $template->error() });
  }

  my $mh = new Net::SMTP($postmaster->{server})
    or throw RWDE::DevelException({ info => 'PostMaster::CONNECT failure ' . $postmaster->{server} });

  $mh->mail($$params{smtp_sender})
    or throw RWDE::DevelException({ info => 'PostMaster::SMTP FROM failure: ' . $postmaster->{server} . '::' . $mh->message() });

  $mh->recipient($$params{smtp_recipient})
    or throw RWDE::DevelException({ info => 'PostMaster::SMTP TO failure: ' . $postmaster->{server} . '::' . $mh->message() . ' for: ' . $$params{smtp_recipient} });

  $mh->data()
    or throw RWDE::DevelException({ info => 'PostMaster::DATA failure: ' . $postmaster->{server} . '::' . $mh->message() });
  $mh->datasend("Errors-To:$$params{smtp_sender}\n")
    or throw RWDE::DevelException({ info => 'PostMaster::DATASEND failure: ' . $postmaster->{server} . '::' . $mh->message() });
  $mh->datasend($output)
    or throw RWDE::DevelException({ info => 'PostMaster::DATASEND failure: ' . $postmaster->{server} . '::' . $mh->message() });

  $mh->dataend()
    or throw RWDE::DevelException({ info => 'PostMaster::DATA END failure, message NOT sent: ' . $postmaster->{server} . '::' . $mh->message() });

  $mh->quit();

  return ();
}

=head2 send_verp_message($smtp_sender, $recipients, $template)

Prepare or send a 1-to-many message to all the addresses contained within $recipients, from $smtp_sender address, using $template as a template input
and the $params to populate the template. To alter the header from/to etc, edit the template.

=cut

sub send_verp_message {
  my ($self, $params) = @_;

  my @failed;

  my @required = qw( smtp_sender recipients template);
  RWDE::RObject->check_params({ required => \@required, supplied => $params });

  my $postmaster = RWDE::PostMaster->get_instance();

  my @recipients = @{ $$params{recipients} };
  if (!(@recipients > 0)) {
    return;
  }

  # Process the message through the Template
  my $output;
  my $template = $postmaster->{template};

  unless ($template->process($$params{template}, $params, \$output)) {
    throw RWDE::DevelException({ info => $template->error() });
  }

  my $mh = new Net::SMTP($postmaster->{server})
    or throw RWDE::DevelException({ info => 'PostMaster::CONNECT failure' });

  #Limit to 1000 recipients per connection
  # if we get more, just bucketize them

  $mh->mail($$params{smtp_sender}, XVERP => 1)
    or throw RWDE::DevelException({ info => 'PostMaster::SMTP XVERP failure: ' . $mh->message() });

  my @good_recipients = $mh->recipient(@recipients, { SkipBad => 1 }) or ();

  $mh->data()
    or throw RWDE::DevelException({ info => 'PostMaster::DATA failure: ' . $mh->message() });

  $mh->datasend($output)
    or throw RWDE::DevelException({ info => 'PostMaster::DATASEND failure: ' . $mh->message() });

  $mh->dataend()
    or throw RWDE::DevelException({ info => 'DATA END failure, message NOT sent: ' . $postmaster->{server} . '::' . $mh->message() });

  $mh->quit();

  return \@good_recipients;
}

=head2 send_support_message($topic, $question)

Prepare or send a message to the support queue requested within $topic, from a Sender (specified in config file) using support.tt as a template input.
The $params are used to to populate the template. To alter the header from/to etc, edit the template.

This is a somewhat niche method utilized with pre-established support systems such as RT.

=cut

sub send_support_message {
  my ($self, $params) = @_;

  my $topic = $$params{topic}
    or throw RWDE::DataBadException({ info => 'No topic selected. Please select a topic so we can route your message properly' });

  my $question = $$params{question}
    or throw RWDE::DataBadException({ info => 'Sorry, we didn\'t receive your message.  Please try sending it again.' });

  $self->send_message(
    {
      smtp_sender    => RWDE::Configuration->Sender,
      smtp_recipient => RWDE::Configuration->$topic,
      template       => 'support.tt',

      #-- template params
      params => $params,
    }
  );

  return ();
}

=head2 send_report_message($topic, $question, $template)

Prepare or send a message to the ErrorReport address specified in the config file, from Sender - also specified in the config file.
Uses report.tt as a template input. The $params are used to to populate the template. To alter the header from/to etc, edit the template.

This method is extremely useful in addressing uncaught exceptions, if the system is configured correctly those messages will get sent 
via this method.

=cut

sub send_report_message {
  my ($self, $params) = @_;

  $$params{smtp_sender}    = RWDE::Configuration->Sender;
  $$params{smtp_recipient} = RWDE::Configuration->ErrorReport;
  $$params{template}       = 'report.tt';

  $self->send_message($params);

  return ();
}

1;